/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.language.visitor;
import static org.teiid.language.SQLConstants.Reserved.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.StringUtil;
import org.teiid.language.AggregateFunction;
import org.teiid.language.AndOr;
import org.teiid.language.Argument;
import org.teiid.language.Call;
import org.teiid.language.ColumnReference;
import org.teiid.language.Command;
import org.teiid.language.Comparison;
import org.teiid.language.Condition;
import org.teiid.language.Delete;
import org.teiid.language.DerivedColumn;
import org.teiid.language.DerivedTable;
import org.teiid.language.Exists;
import org.teiid.language.Expression;
import org.teiid.language.ExpressionValueSource;
import org.teiid.language.Function;
import org.teiid.language.GroupBy;
import org.teiid.language.In;
import org.teiid.language.Insert;
import org.teiid.language.IsNull;
import org.teiid.language.IteratorValueSource;
import org.teiid.language.Join;
import org.teiid.language.LanguageObject;
import org.teiid.language.Like;
import org.teiid.language.Limit;
import org.teiid.language.Literal;
import org.teiid.language.NamedTable;
import org.teiid.language.Not;
import org.teiid.language.OrderBy;
import org.teiid.language.QueryExpression;
import org.teiid.language.ScalarSubquery;
import org.teiid.language.SearchedCase;
import org.teiid.language.SearchedWhenClause;
import org.teiid.language.Select;
import org.teiid.language.SetClause;
import org.teiid.language.SetQuery;
import org.teiid.language.SortSpecification;
import org.teiid.language.SubqueryComparison;
import org.teiid.language.SubqueryIn;
import org.teiid.language.TableReference;
import org.teiid.language.Update;
import org.teiid.language.With;
import org.teiid.language.WithItem;
import org.teiid.language.Argument.Direction;
import org.teiid.language.SQLConstants.NonReserved;
import org.teiid.language.SQLConstants.Tokens;
import org.teiid.language.SortSpecification.Ordering;
import org.teiid.metadata.AbstractMetadataRecord;
/**
* Creates a SQL string for a LanguageObject subtree. Instances of this class
* are not reusable, and are not thread-safe.
*/
public class SQLStringVisitor extends AbstractLanguageVisitor {
private Set<String> infixFunctions = new HashSet<String>(Arrays.asList("%", "+", "-", "*", "+", "/", "||", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
"&", "|", "^", "#")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
protected static final String UNDEFINED = "<undefined>"; //$NON-NLS-1$
protected static final String UNDEFINED_PARAM = "?"; //$NON-NLS-1$
protected StringBuilder buffer = new StringBuilder();
/**
* Gets the name of a group or element from the RuntimeMetadata
* @param id the id of the group or element
* @return the name of that element or group as defined in the source
*/
protected String getName(AbstractMetadataRecord object) {
String nameInSource = object.getNameInSource();
if(nameInSource != null && nameInSource.length() > 0) {
return nameInSource;
}
return object.getName();
}
/**
* Appends the string form of the ILanguageObject to the current buffer.
* @param obj the language object instance
*/
public void append(LanguageObject obj) {
if (obj == null) {
buffer.append(UNDEFINED);
} else {
visitNode(obj);
}
}
/**
* Simple utility to append a list of language objects to the current buffer
* by creating a comma-separated list.
* @param items a list of ILanguageObjects
*/
protected void append(List<? extends LanguageObject> items) {
if (items != null && items.size() != 0) {
append(items.get(0));
for (int i = 1; i < items.size(); i++) {
buffer.append(Tokens.COMMA)
.append(Tokens.SPACE);
append(items.get(i));
}
}
}
/**
* Simple utility to append an array of language objects to the current buffer
* by creating a comma-separated list.
* @param items an array of ILanguageObjects
*/
protected void append(LanguageObject[] items) {
if (items != null && items.length != 0) {
append(items[0]);
for (int i = 1; i < items.length; i++) {
buffer.append(Tokens.COMMA)
.append(Tokens.SPACE);
append(items[i]);
}
}
}
/**
* Creates a SQL-safe string. Simply replaces all occurrences of ' with ''
* @param str the input string
* @return a SQL-safe string
*/
protected String escapeString(String str, String quote) {
return StringUtil.replaceAll(str, quote, quote + quote);
}
public String toString() {
return buffer.toString();
}
public void visit(AggregateFunction obj) {
buffer.append(obj.getName())
.append(Tokens.LPAREN);
if ( obj.isDistinct()) {
buffer.append(DISTINCT)
.append(Tokens.SPACE);
}
if (obj.getExpression() == null) {
buffer.append(Tokens.ALL_COLS);
} else {
append(obj.getExpression());
}
buffer.append(Tokens.RPAREN);
}
public void visit(Comparison obj) {
append(obj.getLeftExpression());
buffer.append(Tokens.SPACE);
buffer.append(obj.getOperator());
buffer.append(Tokens.SPACE);
append(obj.getRightExpression());
}
public void visit(AndOr obj) {
String opString = obj.getOperator().toString();
appendNestedCondition(obj, obj.getLeftCondition());
buffer.append(Tokens.SPACE)
.append(opString)
.append(Tokens.SPACE);
appendNestedCondition(obj, obj.getRightCondition());
}
protected void appendNestedCondition(AndOr parent, Condition condition) {
if (condition instanceof AndOr) {
AndOr nested = (AndOr)condition;
if (nested.getOperator() != parent.getOperator()) {
buffer.append(Tokens.LPAREN);
append(condition);
buffer.append(Tokens.RPAREN);
return;
}
}
append(condition);
}
public void visit(Delete obj) {
buffer.append(DELETE)
.append(Tokens.SPACE);
buffer.append(getSourceComment(obj));
buffer.append(FROM)
.append(Tokens.SPACE);
append(obj.getTable());
if (obj.getWhere() != null) {
buffer.append(Tokens.SPACE)
.append(WHERE)
.append(Tokens.SPACE);
append(obj.getWhere());
}
}
/**
* Take the specified derived group and element short names and determine a
* replacement element name to use instead. Most commonly, this is used to strip
* the group name if the group is a pseudo-group (DUAL) or the element is a pseudo-group
* (ROWNUM). It may also be used to strip special information out of the name in source
* value in some specialized cases.
*
* By default, this method returns null, indicating that the normal group and element
* name logic should be used (group + "." + element). Subclasses should override and
* implement this method if desired.
*
* @param group Group name, may be null
* @param element Element name, never null
* @return Replacement element name to be used as is (no modification will occur)
* @since 5.0
*/
protected String replaceElementName(String group, String element) {
return null;
}
public void visit(ColumnReference obj) {
buffer.append(getElementName(obj, true));
}
private String getElementName(ColumnReference obj, boolean qualify) {
String groupName = null;
NamedTable group = obj.getTable();
if (group != null && qualify) {
if(group.getCorrelationName() != null) {
groupName = group.getCorrelationName();
} else {
AbstractMetadataRecord groupID = group.getMetadataObject();
if(groupID != null) {
groupName = getName(groupID);
} else {
groupName = group.getName();
}
}
}
String elemShortName = null;
AbstractMetadataRecord elementID = obj.getMetadataObject();
if(elementID != null) {
elemShortName = getName(elementID);
} else {
String elementName = obj.getName();
elemShortName = getShortName(elementName);
}
// Check whether a subclass wants to replace the element name to use in special circumstances
String replacementElement = replaceElementName(groupName, elemShortName);
if(replacementElement != null) {
// If so, use it as is
return replacementElement;
}
StringBuffer elementName = new StringBuffer(elemShortName.length());
// If not, do normal logic: [group + "."] + element
if(groupName != null) {
elementName.append(groupName);
elementName.append(Tokens.DOT);
}
elementName.append(elemShortName);
return elementName.toString();
}
/**
* @param elementName
* @return
* @since 4.3
*/
public static String getShortName(String elementName) {
int lastDot = elementName.lastIndexOf("."); //$NON-NLS-1$
if(lastDot >= 0) {
elementName = elementName.substring(lastDot+1);
}
return elementName;
}
public void visit(Call obj) {
buffer.append(EXEC)
.append(Tokens.SPACE);
if(obj.getMetadataObject() != null) {
buffer.append(getName(obj.getMetadataObject()));
} else {
buffer.append(obj.getProcedureName());
}
buffer.append(Tokens.LPAREN);
final List<Argument> params = obj.getArguments();
if (params != null && params.size() != 0) {
Argument param = null;
for (int i = 0; i < params.size(); i++) {
param = params.get(i);
if (param.getDirection() == Direction.IN || param.getDirection() == Direction.INOUT) {
if (i != 0) {
buffer.append(Tokens.COMMA)
.append(Tokens.SPACE);
}
append(param);
}
}
}
buffer.append(Tokens.RPAREN);
}
public void visit(Exists obj) {
buffer.append(EXISTS)
.append(Tokens.SPACE)
.append(Tokens.LPAREN);
append(obj.getSubquery());
buffer.append(Tokens.RPAREN);
}
protected boolean isInfixFunction(String function) {
return infixFunctions.contains(function);
}
public void visit(Function obj) {
String name = obj.getName();
List<Expression> args = obj.getParameters();
if(name.equalsIgnoreCase(CONVERT) || name.equalsIgnoreCase(CAST)) {
Object typeValue = ((Literal)args.get(1)).getValue();
buffer.append(name);
buffer.append(Tokens.LPAREN);
append(args.get(0));
if(name.equalsIgnoreCase(CONVERT)) {
buffer.append(Tokens.COMMA);
buffer.append(Tokens.SPACE);
} else {
buffer.append(Tokens.SPACE);
buffer.append(AS);
buffer.append(Tokens.SPACE);
}
buffer.append(typeValue);
buffer.append(Tokens.RPAREN);
} else if(isInfixFunction(name)) {
buffer.append(Tokens.LPAREN);
if(args != null) {
for(int i=0; i<args.size(); i++) {
append(args.get(i));
if(i < (args.size()-1)) {
buffer.append(Tokens.SPACE);
buffer.append(name);
buffer.append(Tokens.SPACE);
}
}
}
buffer.append(Tokens.RPAREN);
} else if(name.equalsIgnoreCase(NonReserved.TIMESTAMPADD) || name.equalsIgnoreCase(NonReserved.TIMESTAMPDIFF)) {
buffer.append(name);
buffer.append(Tokens.LPAREN);
if(args != null && args.size() > 0) {
buffer.append(((Literal)args.get(0)).getValue());
for(int i=1; i<args.size(); i++) {
buffer.append(Tokens.COMMA);
buffer.append(Tokens.SPACE);
append(args.get(i));
}
}
buffer.append(Tokens.RPAREN);
} else {
buffer.append(obj.getName())
.append(Tokens.LPAREN);
append(obj.getParameters());
buffer.append(Tokens.RPAREN);
}
}
public void visit(NamedTable obj) {
AbstractMetadataRecord groupID = obj.getMetadataObject();
if(groupID != null) {
buffer.append(getName(groupID));
} else {
buffer.append(obj.getName());
}
if (obj.getCorrelationName() != null) {
buffer.append(Tokens.SPACE);
if (useAsInGroupAlias()){
buffer.append(AS)
.append(Tokens.SPACE);
}
buffer.append(obj.getCorrelationName());
}
}
/**
* Indicates whether group alias should be of the form
* "...FROM groupA AS X" or "...FROM groupA X". Certain
* data sources (such as Oracle) may not support the first
* form.
* @return boolean
*/
protected boolean useAsInGroupAlias(){
return true;
}
public void visit(GroupBy obj) {
buffer.append(GROUP)
.append(Tokens.SPACE)
.append(BY)
.append(Tokens.SPACE);
append(obj.getElements());
}
public void visit(In obj) {
append(obj.getLeftExpression());
if (obj.isNegated()) {
buffer.append(Tokens.SPACE)
.append(NOT);
}
buffer.append(Tokens.SPACE)
.append(IN)
.append(Tokens.SPACE)
.append(Tokens.LPAREN);
append(obj.getRightExpressions());
buffer.append(Tokens.RPAREN);
}
public void visit(DerivedTable obj) {
buffer.append(Tokens.LPAREN);
append(obj.getQuery());
buffer.append(Tokens.RPAREN);
buffer.append(Tokens.SPACE);
if(useAsInGroupAlias()) {
buffer.append(AS);
buffer.append(Tokens.SPACE);
}
buffer.append(obj.getCorrelationName());
}
public void visit(Insert obj) {
buffer.append(INSERT).append(Tokens.SPACE);
buffer.append(getSourceComment(obj));
buffer.append(INTO).append(Tokens.SPACE);
append(obj.getTable());
buffer.append(Tokens.SPACE).append(Tokens.LPAREN);
int elementCount = obj.getColumns().size();
for (int i = 0; i < elementCount; i++) {
buffer.append(getElementName(obj.getColumns().get(i), false));
if (i < elementCount - 1) {
buffer.append(Tokens.COMMA);
buffer.append(Tokens.SPACE);
}
}
buffer.append(Tokens.RPAREN);
buffer.append(Tokens.SPACE);
append(obj.getValueSource());
}
@Override
public void visit(ExpressionValueSource obj) {
buffer.append(VALUES).append(Tokens.SPACE).append(Tokens.LPAREN);
append(obj.getValues());
buffer.append(Tokens.RPAREN);
}
@Override
public void visit(IteratorValueSource obj) {
buffer.append(VALUES).append(Tokens.SPACE).append(Tokens.LPAREN);
for (int i = 0; i < obj.getColumnCount(); i++) {
if (i > 0) {
buffer.append(", "); //$NON-NLS-1$
}
buffer.append("?"); //$NON-NLS-1$
}
buffer.append(Tokens.RPAREN);
}
public void visit(IsNull obj) {
append(obj.getExpression());
buffer.append(Tokens.SPACE)
.append(IS)
.append(Tokens.SPACE);
if (obj.isNegated()) {
buffer.append(NOT)
.append(Tokens.SPACE);
}
buffer.append(NULL);
}
public void visit(Join obj) {
TableReference leftItem = obj.getLeftItem();
if(useParensForJoins() && leftItem instanceof Join) {
buffer.append(Tokens.LPAREN);
append(leftItem);
buffer.append(Tokens.RPAREN);
} else {
append(leftItem);
}
buffer.append(Tokens.SPACE);
switch(obj.getJoinType()) {
case CROSS_JOIN:
buffer.append(CROSS);
break;
case FULL_OUTER_JOIN:
buffer.append(FULL)
.append(Tokens.SPACE)
.append(OUTER);
break;
case INNER_JOIN:
buffer.append(INNER);
break;
case LEFT_OUTER_JOIN:
buffer.append(LEFT)
.append(Tokens.SPACE)
.append(OUTER);
break;
case RIGHT_OUTER_JOIN:
buffer.append(RIGHT)
.append(Tokens.SPACE)
.append(OUTER);
break;
default: buffer.append(UNDEFINED);
}
buffer.append(Tokens.SPACE)
.append(JOIN)
.append(Tokens.SPACE);
TableReference rightItem = obj.getRightItem();
if(rightItem instanceof Join && (useParensForJoins() || obj.getJoinType() == Join.JoinType.CROSS_JOIN)) {
buffer.append(Tokens.LPAREN);
append(rightItem);
buffer.append(Tokens.RPAREN);
} else {
append(rightItem);
}
final Condition condition = obj.getCondition();
if (condition != null) {
buffer.append(Tokens.SPACE)
.append(ON)
.append(Tokens.SPACE);
append(condition);
}
}
public void visit(Like obj) {
append(obj.getLeftExpression());
if (obj.isNegated()) {
buffer.append(Tokens.SPACE)
.append(NOT);
}
buffer.append(Tokens.SPACE)
.append(LIKE)
.append(Tokens.SPACE);
append(obj.getRightExpression());
if (obj.getEscapeCharacter() != null) {
buffer.append(Tokens.SPACE)
.append(ESCAPE)
.append(Tokens.SPACE)
.append(Tokens.QUOTE)
.append(obj.getEscapeCharacter().toString())
.append(Tokens.QUOTE);
}
}
public void visit(Limit obj) {
buffer.append(LIMIT)
.append(Tokens.SPACE);
if (obj.getRowOffset() > 0) {
buffer.append(obj.getRowOffset())
.append(Tokens.COMMA)
.append(Tokens.SPACE);
}
buffer.append(obj.getRowLimit());
}
public void visit(Literal obj) {
if (obj.isBindValue()) {
buffer.append("?"); //$NON-NLS-1$
} else if (obj.getValue() == null) {
buffer.append(NULL);
} else {
Class<?> type = obj.getType();
String val = obj.getValue().toString();
if(Number.class.isAssignableFrom(type)) {
buffer.append(val);
} else if(type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) {
buffer.append(obj.getValue().equals(Boolean.TRUE) ? TRUE : FALSE);
} else if(type.equals(DataTypeManager.DefaultDataClasses.TIMESTAMP)) {
buffer.append("{ts '") //$NON-NLS-1$
.append(val)
.append("'}"); //$NON-NLS-1$
} else if(type.equals(DataTypeManager.DefaultDataClasses.TIME)) {
buffer.append("{t '") //$NON-NLS-1$
.append(val)
.append("'}"); //$NON-NLS-1$
} else if(type.equals(DataTypeManager.DefaultDataClasses.DATE)) {
buffer.append("{d '") //$NON-NLS-1$
.append(val)
.append("'}"); //$NON-NLS-1$
} else {
buffer.append(Tokens.QUOTE)
.append(escapeString(val, Tokens.QUOTE))
.append(Tokens.QUOTE);
}
}
}
public void visit(Not obj) {
buffer.append(NOT)
.append(Tokens.SPACE)
.append(Tokens.LPAREN);
append(obj.getCriteria());
buffer.append(Tokens.RPAREN);
}
public void visit(OrderBy obj) {
buffer.append(ORDER)
.append(Tokens.SPACE)
.append(BY)
.append(Tokens.SPACE);
append(obj.getSortSpecifications());
}
public void visit(SortSpecification obj) {
append(obj.getExpression());
if (obj.getOrdering() == Ordering.DESC) {
buffer.append(Tokens.SPACE)
.append(DESC);
} // Don't print default "ASC"
if (obj.getNullOrdering() != null) {
buffer.append(Tokens.SPACE)
.append(NonReserved.NULLS)
.append(Tokens.SPACE)
.append(obj.getNullOrdering().name());
}
}
public void visit(Argument obj) {
buffer.append(obj.getArgumentValue());
}
public void visit(Select obj) {
if (obj.getWith() != null) {
append(obj.getWith());
}
buffer.append(SELECT).append(Tokens.SPACE);
buffer.append(getSourceComment(obj));
if (obj.isDistinct()) {
buffer.append(DISTINCT).append(Tokens.SPACE);
}
if (useSelectLimit() && obj.getLimit() != null) {
append(obj.getLimit());
buffer.append(Tokens.SPACE);
}
append(obj.getDerivedColumns());
if (obj.getFrom() != null && !obj.getFrom().isEmpty()) {
buffer.append(Tokens.SPACE).append(FROM).append(Tokens.SPACE);
append(obj.getFrom());
}
if (obj.getWhere() != null) {
buffer.append(Tokens.SPACE)
.append(WHERE)
.append(Tokens.SPACE);
append(obj.getWhere());
}
if (obj.getGroupBy() != null) {
buffer.append(Tokens.SPACE);
append(obj.getGroupBy());
}
if (obj.getHaving() != null) {
buffer.append(Tokens.SPACE)
.append(HAVING)
.append(Tokens.SPACE);
append(obj.getHaving());
}
if (obj.getOrderBy() != null) {
buffer.append(Tokens.SPACE);
append(obj.getOrderBy());
}
if (!useSelectLimit() && obj.getLimit() != null) {
buffer.append(Tokens.SPACE);
append(obj.getLimit());
}
}
public void visit(SearchedCase obj) {
buffer.append(CASE);
for (SearchedWhenClause swc : obj.getCases()) {
append(swc);
}
if (obj.getElseExpression() != null) {
buffer.append(Tokens.SPACE)
.append(ELSE)
.append(Tokens.SPACE);
append(obj.getElseExpression());
}
buffer.append(Tokens.SPACE)
.append(END);
}
@Override
public void visit(SearchedWhenClause obj) {
buffer.append(Tokens.SPACE).append(WHEN)
.append(Tokens.SPACE);
append(obj.getCondition());
buffer.append(Tokens.SPACE).append(THEN)
.append(Tokens.SPACE);
append(obj.getResult());
}
protected String getSourceComment(Command command) {
return ""; //$NON-NLS-1$
}
public void visit(ScalarSubquery obj) {
buffer.append(Tokens.LPAREN);
append(obj.getSubquery());
buffer.append(Tokens.RPAREN);
}
public void visit(DerivedColumn obj) {
append(obj.getExpression());
if (obj.getAlias() != null) {
buffer.append(Tokens.SPACE)
.append(AS)
.append(Tokens.SPACE)
.append(obj.getAlias());
}
}
public void visit(SubqueryComparison obj) {
append(obj.getLeftExpression());
buffer.append(Tokens.SPACE);
switch(obj.getOperator()) {
case EQ: buffer.append(Tokens.EQ); break;
case GE: buffer.append(Tokens.GE); break;
case GT: buffer.append(Tokens.GT); break;
case LE: buffer.append(Tokens.LE); break;
case LT: buffer.append(Tokens.LT); break;
case NE: buffer.append(Tokens.NE); break;
default: buffer.append(UNDEFINED);
}
buffer.append(Tokens.SPACE);
switch(obj.getQuantifier()) {
case ALL: buffer.append(ALL); break;
case SOME: buffer.append(SOME); break;
default: buffer.append(UNDEFINED);
}
buffer.append(Tokens.SPACE);
buffer.append(Tokens.LPAREN);
append(obj.getSubquery());
buffer.append(Tokens.RPAREN);
}
public void visit(SubqueryIn obj) {
append(obj.getLeftExpression());
if (obj.isNegated()) {
buffer.append(Tokens.SPACE)
.append(NOT);
}
buffer.append(Tokens.SPACE)
.append(IN)
.append(Tokens.SPACE)
.append(Tokens.LPAREN);
append(obj.getSubquery());
buffer.append(Tokens.RPAREN);
}
public void visit(Update obj) {
buffer.append(UPDATE)
.append(Tokens.SPACE);
buffer.append(getSourceComment(obj));
append(obj.getTable());
buffer.append(Tokens.SPACE)
.append(SET)
.append(Tokens.SPACE);
append(obj.getChanges());
if (obj.getWhere() != null) {
buffer.append(Tokens.SPACE)
.append(WHERE)
.append(Tokens.SPACE);
append(obj.getWhere());
}
}
public void visit(SetClause clause) {
buffer.append(getElementName(clause.getSymbol(), false));
buffer.append(Tokens.SPACE).append(Tokens.EQ).append(Tokens.SPACE);
append(clause.getValue());
}
public void visit(SetQuery obj) {
if (obj.getWith() != null) {
append(obj.getWith());
}
appendSetQuery(obj, obj.getLeftQuery(), false);
buffer.append(Tokens.SPACE);
appendSetOperation(obj.getOperation());
if(obj.isAll()) {
buffer.append(Tokens.SPACE);
buffer.append(ALL);
}
buffer.append(Tokens.SPACE);
appendSetQuery(obj, obj.getRightQuery(), true);
OrderBy orderBy = obj.getOrderBy();
if(orderBy != null) {
buffer.append(Tokens.SPACE);
append(orderBy);
}
Limit limit = obj.getLimit();
if(limit != null) {
buffer.append(Tokens.SPACE);
append(limit);
}
}
protected void appendSetOperation(SetQuery.Operation operation) {
buffer.append(operation);
}
protected boolean useParensForSetQueries() {
return false;
}
protected void appendSetQuery(SetQuery parent, QueryExpression obj, boolean right) {
if((!(obj instanceof SetQuery) && useParensForSetQueries())
|| (right && ((obj instanceof SetQuery
&& ((parent.isAll() && !((SetQuery)obj).isAll())
|| parent.getOperation() != ((SetQuery)obj).getOperation())) || obj.getLimit() != null || obj.getOrderBy() != null))) {
buffer.append(Tokens.LPAREN);
append(obj);
buffer.append(Tokens.RPAREN);
} else {
if (!parent.isAll() && obj instanceof SetQuery) {
((SetQuery)obj).setAll(false);
}
append(obj);
}
}
@Override
public void visit(With obj) {
buffer.append(WITH);
buffer.append(Tokens.SPACE);
append(obj.getItems());
}
@Override
public void visit(WithItem obj) {
append(obj.getTable());
buffer.append(Tokens.SPACE);
if (obj.getColumns() != null) {
buffer.append(Tokens.LPAREN);
append(obj.getColumns());
buffer.append(Tokens.RPAREN);
buffer.append(Tokens.SPACE);
}
buffer.append(AS);
buffer.append(Tokens.LPAREN);
append(obj.getSubquery());
buffer.append(Tokens.RPAREN);
}
/**
* Gets the SQL string representation for a given ILanguageObject.
* @param obj the root of the ILanguageObject hierarchy that needs to be
* converted. This can be any subtree, and does not need to be a top-level
* command
* @return the SQL representation of that ILanguageObject hierarchy
*/
public static String getSQLString(LanguageObject obj) {
SQLStringVisitor visitor = new SQLStringVisitor();
visitor.append(obj);
return visitor.toString();
}
protected boolean useParensForJoins() {
return false;
}
protected boolean useSelectLimit() {
return false;
}
}