/* * 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.query.sql.visitor; import static org.teiid.language.SQLConstants.Reserved.*; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.teiid.core.types.ArrayImpl; import org.teiid.core.types.DataTypeManager; import org.teiid.core.util.PropertiesUtils; import org.teiid.core.util.StringUtil; import org.teiid.language.SQLConstants; import org.teiid.language.SQLConstants.NonReserved; import org.teiid.language.SQLConstants.Reserved; import org.teiid.language.SQLConstants.Tokens; import org.teiid.metadata.BaseColumn.NullType; import org.teiid.metadata.Column; import org.teiid.query.metadata.DDLStringVisitor; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.LanguageVisitor; import org.teiid.query.sql.lang.*; import org.teiid.query.sql.lang.Create.CommitAction; import org.teiid.query.sql.lang.ExistsCriteria.SubqueryHint; import org.teiid.query.sql.lang.ObjectTable.ObjectColumn; import org.teiid.query.sql.lang.Option.MakeDep; import org.teiid.query.sql.lang.SourceHint.SpecificHint; import org.teiid.query.sql.lang.TableFunctionReference.ProjectedColumn; import org.teiid.query.sql.lang.TextTable.TextColumn; import org.teiid.query.sql.lang.XMLTable.XMLColumn; import org.teiid.query.sql.proc.*; import org.teiid.query.sql.proc.Statement.Labeled; import org.teiid.query.sql.symbol.*; import org.teiid.query.sql.symbol.AggregateSymbol.Type; import org.teiid.query.sql.symbol.XMLNamespaces.NamespaceItem; import org.teiid.translator.SourceSystemFunctions; /** * <p> * The SQLStringVisitor will visit a set of language objects and return the corresponding SQL string representation. * </p> */ public class SQLStringVisitor extends LanguageVisitor { private class DDLVisitor extends DDLStringVisitor { private DDLVisitor() { super(null, null); this.usePrefixes = false; this.createNS = false; } @Override protected DDLStringVisitor append(Object o) { SQLStringVisitor.this.append(o); return this; } } public static final String UNDEFINED = "<undefined>"; //$NON-NLS-1$ private static final String SPACE = " "; //$NON-NLS-1$ private static final String BEGIN_HINT = "/*+"; //$NON-NLS-1$ private static final String END_HINT = "*/"; //$NON-NLS-1$ private static final char ID_ESCAPE_CHAR = '\"'; private static final Set<String> INFIX_FUNCTIONS = new HashSet<String>(Arrays.asList(SQLConstants.Tokens.PLUS, SQLConstants.Tokens.MINUS, SQLConstants.Tokens.ALL_COLS, SQLConstants.Tokens.SLASH, SQLConstants.Tokens.CONCAT, SQLConstants.Tokens.DOUBLE_AMP)); protected StringBuilder parts = new StringBuilder(); private boolean shortNameOnly = false; /** * Helper to quickly get the parser string for an object using the visitor. * * @param obj Language object * @return String SQL String for obj */ public static final String getSQLString( LanguageObject obj ) { if (obj == null) { return UNDEFINED; } SQLStringVisitor visitor = new SQLStringVisitor(); obj.acceptVisitor(visitor); return visitor.getSQLString(); } /** * Retrieve completed string from the visitor. * * @return Complete SQL string for the visited nodes */ public String getSQLString() { return this.parts.toString(); } protected void visitNode( LanguageObject obj ) { if (obj == null) { append(UNDEFINED); return; } obj.acceptVisitor(this); } protected void append( Object value ) { this.parts.append(value); } protected void beginClause( @SuppressWarnings("unused") int level ) { append(SPACE); } // ############ Visitor methods for language objects #################### @Override public void visit( BetweenCriteria obj ) { visitNode(obj.getExpression()); append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } append(BETWEEN); append(SPACE); visitNode(obj.getLowerExpression()); append(SPACE); append(AND); append(SPACE); visitNode(obj.getUpperExpression()); } @Override public void visit( CaseExpression obj ) { append(CASE); append(SPACE); visitNode(obj.getExpression()); append(SPACE); for (int i = 0; i < obj.getWhenCount(); i++) { append(WHEN); append(SPACE); visitNode(obj.getWhenExpression(i)); append(SPACE); append(THEN); append(SPACE); visitNode(obj.getThenExpression(i)); append(SPACE); } if (obj.getElseExpression() != null) { append(ELSE); append(SPACE); visitNode(obj.getElseExpression()); append(SPACE); } append(END); } @Override public void visit( CompareCriteria obj ) { Expression leftExpression = obj.getLeftExpression(); if (leftExpression instanceof CompareCriteria) { append(Tokens.LPAREN); visitNode(leftExpression); append(Tokens.RPAREN); } else { visitNode(leftExpression); } append(SPACE); append(obj.getOperatorAsString()); append(SPACE); Expression rightExpression = obj.getRightExpression(); if (rightExpression instanceof CompareCriteria) { append(Tokens.LPAREN); visitNode(rightExpression); append(Tokens.RPAREN); } else { visitNode(rightExpression); } } @Override public void visit( CompoundCriteria obj ) { // Get operator string int operator = obj.getOperator(); String operatorStr = ""; //$NON-NLS-1$ if (operator == CompoundCriteria.AND) { operatorStr = AND; } else if (operator == CompoundCriteria.OR) { operatorStr = OR; } // Get criteria List<Criteria> subCriteria = obj.getCriteria(); // Build parts if (subCriteria.size() == 1) { // Special case - should really never happen, but we are tolerant Criteria firstChild = subCriteria.get(0); visitNode(firstChild); } else { // Add first criteria Iterator<Criteria> iter = subCriteria.iterator(); while (iter.hasNext()) { // Add criteria Criteria crit = iter.next(); append(Tokens.LPAREN); visitNode(crit); append(Tokens.RPAREN); if (iter.hasNext()) { // Add connector append(SPACE); append(operatorStr); append(SPACE); } } } } @Override public void visit( Delete obj ) { // add delete clause append(DELETE); addSourceHint(obj.getSourceHint()); append(SPACE); // add from clause append(FROM); append(SPACE); visitNode(obj.getGroup()); // add where clause if (obj.getCriteria() != null) { beginClause(0); visitCriteria(WHERE, obj.getCriteria()); } // Option clause if (obj.getOption() != null) { beginClause(0); visitNode(obj.getOption()); } } @Override public void visit( DependentSetCriteria obj ) { visitNode(obj.getExpression()); // operator and beginning of list append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } append(IN); append(" (<dependent values>)"); //$NON-NLS-1$ } @Override public void visit( From obj ) { append(FROM); beginClause(1); registerNodes(obj.getClauses(), 0); } @Override public void visit( GroupBy obj ) { append(GROUP); append(SPACE); append(BY); append(SPACE); if (obj.isRollup()) { append(ROLLUP); append(Tokens.LPAREN); } registerNodes(obj.getSymbols(), 0); if (obj.isRollup()) { append(Tokens.RPAREN); } } @Override public void visit( Insert obj ) { if (obj.isUpsert()) { append(NonReserved.UPSERT); } else { append(INSERT); } addSourceHint(obj.getSourceHint()); append(SPACE); append(INTO); append(SPACE); visitNode(obj.getGroup()); if (!obj.getVariables().isEmpty()) { beginClause(2); // Columns clause List<ElementSymbol> vars = obj.getVariables(); if (vars != null) { append("("); //$NON-NLS-1$ this.shortNameOnly = true; registerNodes(vars, 0); this.shortNameOnly = false; append(")"); //$NON-NLS-1$ } } beginClause(1); if (obj.getQueryExpression() != null) { visitNode(obj.getQueryExpression()); } else if (obj.getTupleSource() != null) { append(VALUES); append(" (...)"); //$NON-NLS-1$ } else if (obj.getValues() != null) { append(VALUES); beginClause(2); append("("); //$NON-NLS-1$ registerNodes(obj.getValues(), 0); append(")"); //$NON-NLS-1$ } // Option clause if (obj.getOption() != null) { beginClause(1); visitNode(obj.getOption()); } } @Override public void visit( Create obj ) { append(CREATE); append(SPACE); if (obj.getTableMetadata() != null) { append(FOREIGN); append(SPACE); append(TEMPORARY); append(SPACE); append(TABLE); append(SPACE); new DDLVisitor().addTableBody(obj.getTableMetadata()); append(SPACE); append(ON); append(SPACE); outputLiteral(String.class, false, obj.getOn()); return; } append(LOCAL); append(SPACE); append(TEMPORARY); append(SPACE); append(TABLE); append(SPACE); visitNode(obj.getTable()); append(SPACE); // Columns clause List<Column> columns = obj.getColumns(); append("("); //$NON-NLS-1$ Iterator<Column> iter = columns.iterator(); while (iter.hasNext()) { Column element = iter.next(); outputDisplayName(element.getName()); append(SPACE); if (element.isAutoIncremented()) { append(NonReserved.SERIAL); } else { append(element.getRuntimeType()); if (element.getNullType() == NullType.No_Nulls) { append(SPACE); append(NOT); append(SPACE); append(NULL); } } if (iter.hasNext()) { append(", "); //$NON-NLS-1$ } } if (!obj.getPrimaryKey().isEmpty()) { append(", "); //$NON-NLS-1$ append(PRIMARY); append(" "); //$NON-NLS-1$ append(NonReserved.KEY); append(Tokens.LPAREN); Iterator<ElementSymbol> pkiter = obj.getPrimaryKey().iterator(); while (pkiter.hasNext()) { outputShortName(pkiter.next()); if (pkiter.hasNext()) { append(", "); //$NON-NLS-1$ } } append(Tokens.RPAREN); } append(Tokens.RPAREN); CommitAction commitAction = obj.getCommitAction(); if (commitAction != null) { append(Tokens.SPACE); append(Reserved.ON); append(Tokens.SPACE); append(Reserved.COMMIT); append(Tokens.SPACE); switch (commitAction) { case PRESERVE_ROWS: append(NonReserved.PRESERVE); append(Tokens.SPACE); append(Reserved.ROWS); break; } } } @Override public void visit( Drop obj ) { append(DROP); append(SPACE); append(TABLE); append(SPACE); visitNode(obj.getTable()); } @Override public void visit( IsNullCriteria obj ) { Expression expr = obj.getExpression(); appendNested(expr); append(SPACE); append(IS); append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } append(NULL); } @Override public void visit( JoinPredicate obj ) { addHintComment(obj); if (obj.hasHint()) { append("(");//$NON-NLS-1$ } // left clause FromClause leftClause = obj.getLeftClause(); if (leftClause instanceof JoinPredicate && !((JoinPredicate)leftClause).hasHint()) { append("("); //$NON-NLS-1$ visitNode(leftClause); append(")"); //$NON-NLS-1$ } else { visitNode(leftClause); } // join type append(SPACE); visitNode(obj.getJoinType()); append(SPACE); // right clause FromClause rightClause = obj.getRightClause(); if (rightClause instanceof JoinPredicate && !((JoinPredicate)rightClause).hasHint()) { append("("); //$NON-NLS-1$ visitNode(rightClause); append(")"); //$NON-NLS-1$ } else { visitNode(rightClause); } // join criteria List joinCriteria = obj.getJoinCriteria(); if (joinCriteria != null && joinCriteria.size() > 0) { append(SPACE); append(ON); append(SPACE); Iterator critIter = joinCriteria.iterator(); while (critIter.hasNext()) { Criteria crit = (Criteria)critIter.next(); if (crit instanceof PredicateCriteria || crit instanceof AtomicCriteria) { visitNode(crit); } else { append("("); //$NON-NLS-1$ visitNode(crit); append(")"); //$NON-NLS-1$ } if (critIter.hasNext()) { append(SPACE); append(AND); append(SPACE); } } } if (obj.hasHint()) { append(")"); //$NON-NLS-1$ } } private void addHintComment( FromClause obj ) { if (obj.hasHint()) { append(BEGIN_HINT); append(SPACE); if (obj.isOptional()) { append(Option.OPTIONAL); append(SPACE); } if (obj.getMakeDep() != null) { append(Option.MAKEDEP); appendMakeDepOptions(obj.getMakeDep()); append(SPACE); } if (obj.isMakeNotDep()) { append(Option.MAKENOTDEP); append(SPACE); } if (obj.getMakeInd() != null) { append(MAKEIND); appendMakeDepOptions(obj.getMakeInd()); append(SPACE); } if (obj.isNoUnnest()) { append(SubqueryHint.NOUNNEST); append(SPACE); } if (obj.isPreserve()) { append(FromClause.PRESERVE); append(SPACE); } append(END_HINT); append(SPACE); } } @Override public void visit( JoinType obj ) { String[] output = null; if (obj.equals(JoinType.JOIN_INNER)) { output = new String[] {INNER, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_CROSS)) { output = new String[] {CROSS, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_LEFT_OUTER)) { output = new String[] {LEFT, SPACE, OUTER, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_RIGHT_OUTER)) { output = new String[] {RIGHT, SPACE, OUTER, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_FULL_OUTER)) { output = new String[] {FULL, SPACE, OUTER, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_UNION)) { output = new String[] {UNION, SPACE, JOIN}; } else if (obj.equals(JoinType.JOIN_SEMI)) { output = new String[] {"SEMI", SPACE, JOIN}; //$NON-NLS-1$ } else if (obj.equals(JoinType.JOIN_ANTI_SEMI)) { output = new String[] {"ANTI SEMI", SPACE, JOIN}; //$NON-NLS-1$ } else { throw new AssertionError(); } for (String part : output) { append(part); } } @Override public void visit( MatchCriteria obj ) { visitNode(obj.getLeftExpression()); append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } switch (obj.getMode()) { case SIMILAR: append(SIMILAR); append(SPACE); append(TO); break; case LIKE: append(LIKE); break; case REGEX: append(LIKE_REGEX); break; } append(SPACE); visitNode(obj.getRightExpression()); if (obj.getEscapeChar() != MatchCriteria.NULL_ESCAPE_CHAR) { append(SPACE); append(ESCAPE); append(SPACE); outputLiteral(String.class, false, obj.getEscapeChar()); } } @Override public void visit( NotCriteria obj ) { append(NOT); append(" ("); //$NON-NLS-1$ visitNode(obj.getCriteria()); append(")"); //$NON-NLS-1$ } @Override public void visit( Option obj ) { append(OPTION); Collection<String> groups = obj.getDependentGroups(); if (groups != null && groups.size() > 0) { append(" "); //$NON-NLS-1$ append(MAKEDEP); append(" "); //$NON-NLS-1$ Iterator<String> iter = groups.iterator(); Iterator<MakeDep> iter1 = obj.getMakeDepOptions().iterator(); while (iter.hasNext()) { outputDisplayName(iter.next()); appendMakeDepOptions(iter1.next()); if (iter.hasNext()) { append(", ");//$NON-NLS-1$ } } } groups = obj.getNotDependentGroups(); if (groups != null && groups.size() > 0) { append(" "); //$NON-NLS-1$ append(MAKENOTDEP); append(" "); //$NON-NLS-1$ Iterator<String> iter = groups.iterator(); while (iter.hasNext()) { outputDisplayName(iter.next()); if (iter.hasNext()) { append(", ");//$NON-NLS-1$ } } } groups = obj.getNoCacheGroups(); if (groups != null && groups.size() > 0) { append(" "); //$NON-NLS-1$ append(NOCACHE); append(" "); //$NON-NLS-1$ Iterator<String> iter = groups.iterator(); while (iter.hasNext()) { outputDisplayName(iter.next()); if (iter.hasNext()) { append(", ");//$NON-NLS-1$ } } } else if (obj.isNoCache()) { append(" "); //$NON-NLS-1$ append(NOCACHE); } } public SQLStringVisitor appendMakeDepOptions(MakeDep makedep) { boolean parens = false; if (makedep.getMax() != null || makedep.getJoin() != null) { append(Tokens.LPAREN); parens = true; } boolean space = false; if (makedep.getMax() != null) { if (space) { append(SPACE); } else { space = true; } append(NonReserved.MAX); append(Tokens.COLON); append(makedep.getMax()); } if (makedep.getJoin() != null) { if (space) { append(SPACE); } else { space = true; } if (!makedep.getJoin()) { append(NO); append(SPACE); } append(JOIN); } if (parens) { append(Tokens.RPAREN); } return this; } @Override public void visit( OrderBy obj ) { append(ORDER); append(SPACE); append(BY); append(SPACE); registerNodes(obj.getOrderByItems(), 0); } @Override public void visit( OrderByItem obj ) { Expression ses = obj.getSymbol(); if (ses instanceof AliasSymbol) { AliasSymbol as = (AliasSymbol)ses; outputDisplayName(as.getOutputName()); } else { visitNode(ses); } if (!obj.isAscending()) { append(SPACE); append(DESC); } // Don't print default "ASC" if (obj.getNullOrdering() != null) { append(SPACE); append(NonReserved.NULLS); append(SPACE); append(obj.getNullOrdering().name()); } } @Override public void visit( DynamicCommand obj ) { append(EXECUTE); append(SPACE); append(IMMEDIATE); append(SPACE); visitNode(obj.getSql()); if (obj.isAsClauseSet()) { beginClause(1); append(AS); append(SPACE); for (int i = 0; i < obj.getAsColumns().size(); i++) { ElementSymbol symbol = (ElementSymbol)obj.getAsColumns().get(i); outputShortName(symbol); append(SPACE); append(DataTypeManager.getDataTypeName(symbol.getType())); if (i < obj.getAsColumns().size() - 1) { append(", "); //$NON-NLS-1$ } } } if (obj.getIntoGroup() != null) { beginClause(1); append(INTO); append(SPACE); visitNode(obj.getIntoGroup()); } if (obj.getUsing() != null && !obj.getUsing().isEmpty()) { beginClause(1); append(USING); append(SPACE); visitNode(obj.getUsing()); } if (obj.getUpdatingModelCount() > 0) { beginClause(1); append(UPDATE); append(SPACE); if (obj.getUpdatingModelCount() > 1) { append("*"); //$NON-NLS-1$ } else { append("1"); //$NON-NLS-1$ } } } @Override public void visit( SetClauseList obj ) { for (Iterator<SetClause> iterator = obj.getClauses().iterator(); iterator.hasNext();) { SetClause clause = iterator.next(); visitNode(clause); if (iterator.hasNext()) { append(", "); //$NON-NLS-1$ } } } @Override public void visit( SetClause obj ) { ElementSymbol symbol = obj.getSymbol(); outputShortName(symbol); append(" = "); //$NON-NLS-1$ visitNode(obj.getValue()); } @Override public void visit(WithQueryCommand obj) { visitNode(obj.getGroupSymbol()); append(SPACE); if (obj.getColumns() != null && !obj.getColumns().isEmpty()) { append(Tokens.LPAREN); shortNameOnly = true; registerNodes(obj.getColumns(), 0); shortNameOnly = false; append(Tokens.RPAREN); append(SPACE); } append(AS); if (obj.isMaterialize()) { append(SPACE); append(BEGIN_HINT); append(SPACE); append(WithQueryCommand.MATERIALIZE); append(SPACE); append(END_HINT); } else if (obj.isNoInline()) { append(SPACE); append(BEGIN_HINT); append(SPACE); append(WithQueryCommand.NO_INLINE); append(SPACE); append(END_HINT); } append(SPACE); append(Tokens.LPAREN); if (obj.getCommand() == null) { append("<dependent values>"); //$NON-NLS-1$ } else { visitNode(obj.getCommand()); } append(Tokens.RPAREN); } @Override public void visit( Query obj ) { addCacheHint(obj.getCacheHint()); addWithClause(obj); append(SELECT); SourceHint sh = obj.getSourceHint(); addSourceHint(sh); if (obj.getSelect() != null) { visitNode(obj.getSelect()); } if (obj.getInto() != null) { beginClause(1); visitNode(obj.getInto()); } if (obj.getFrom() != null) { beginClause(1); visitNode(obj.getFrom()); } // Where clause if (obj.getCriteria() != null) { beginClause(1); visitCriteria(WHERE, obj.getCriteria()); } // Group by clause if (obj.getGroupBy() != null) { beginClause(1); visitNode(obj.getGroupBy()); } // Having clause if (obj.getHaving() != null) { beginClause(1); visitCriteria(HAVING, obj.getHaving()); } // Order by clause if (obj.getOrderBy() != null) { beginClause(1); visitNode(obj.getOrderBy()); } if (obj.getLimit() != null) { beginClause(1); visitNode(obj.getLimit()); } // Option clause if (obj.getOption() != null) { beginClause(1); visitNode(obj.getOption()); } } private void addSourceHint(SourceHint sh) { if (sh != null) { append(SPACE); append(BEGIN_HINT); append("sh"); //$NON-NLS-1$ if (sh.isUseAliases()) { append(SPACE); append("KEEP ALIASES"); //$NON-NLS-1$ } if (sh.getGeneralHint() != null) { appendSourceHintValue(sh.getGeneralHint()); } else { append(SPACE); } if (sh.getSpecificHints() != null) { for (Map.Entry<String, SpecificHint> entry : sh.getSpecificHints().entrySet()) { append(entry.getKey()); if (entry.getValue().isUseAliases()) { append(SPACE); append("KEEP ALIASES"); //$NON-NLS-1$ } appendSourceHintValue(entry.getValue().getHint()); } } append(END_HINT); } } private void addWithClause(QueryCommand obj) { if (obj.getWith() != null) { append(WITH); append(SPACE); registerNodes(obj.getWith(), 0); beginClause(0); } } protected void visitCriteria( String keyWord, Criteria crit ) { append(keyWord); append(SPACE); visitNode(crit); } @Override public void visit( SearchedCaseExpression obj ) { append(CASE); for (int i = 0; i < obj.getWhenCount(); i++) { append(SPACE); append(WHEN); append(SPACE); visitNode(obj.getWhenCriteria(i)); append(SPACE); append(THEN); append(SPACE); visitNode(obj.getThenExpression(i)); } append(SPACE); if (obj.getElseExpression() != null) { append(ELSE); append(SPACE); visitNode(obj.getElseExpression()); append(SPACE); } append(END); } @Override public void visit( Select obj ) { if (obj.isDistinct()) { append(SPACE); append(DISTINCT); } beginClause(2); Iterator<Expression> iter = obj.getSymbols().iterator(); while (iter.hasNext()) { Expression symbol = iter.next(); visitNode(symbol); if (iter.hasNext()) { append(", "); //$NON-NLS-1$ } } } private void appendSourceHintValue(String sh) { append(Tokens.COLON); append('\''); append(escapeStringValue(sh, "'")); //$NON-NLS-1$ append('\''); append(SPACE); } @Override public void visit( SetCriteria obj ) { // variable appendNested(obj.getExpression()); // operator and beginning of list append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } append(IN); append(" ("); //$NON-NLS-1$ // value list Collection vals = obj.getValues(); int size = vals.size(); if (size == 1) { Iterator iter = vals.iterator(); Expression expr = (Expression)iter.next(); visitNode(expr); } else if (size > 1) { Iterator iter = vals.iterator(); Expression expr = (Expression)iter.next(); visitNode(expr); while (iter.hasNext()) { expr = (Expression)iter.next(); append(", "); //$NON-NLS-1$ visitNode(expr); } } append(")"); //$NON-NLS-1$ } /** * Condition operators have lower precedence than LIKE/SIMILAR/IS * @param ex */ private void appendNested(Expression ex) { boolean useParens = ex instanceof Criteria; if (useParens) { append(Tokens.LPAREN); } visitNode(ex); if (useParens) { append(Tokens.RPAREN); } } @Override public void visit( SetQuery obj ) { addCacheHint(obj.getCacheHint()); addWithClause(obj); QueryCommand query = obj.getLeftQuery(); appendSetQuery(obj, query, false); beginClause(0); append(obj.getOperation()); if (obj.isAll()) { append(SPACE); append(ALL); } beginClause(0); query = obj.getRightQuery(); appendSetQuery(obj, query, true); if (obj.getOrderBy() != null) { beginClause(0); visitNode(obj.getOrderBy()); } if (obj.getLimit() != null) { beginClause(0); visitNode(obj.getLimit()); } if (obj.getOption() != null) { beginClause(0); visitNode(obj.getOption()); } } protected void appendSetQuery( SetQuery parent, QueryCommand obj, boolean right ) { if (obj.getLimit() != null || obj.getOrderBy() != null || (right && ((obj instanceof SetQuery && ((parent.isAll() && !((SetQuery)obj).isAll()) || parent.getOperation() != ((SetQuery)obj).getOperation()))))) { append(Tokens.LPAREN); visitNode(obj); append(Tokens.RPAREN); } else { visitNode(obj); } } @Override public void visit( StoredProcedure obj ) { addCacheHint(obj.getCacheHint()); if (obj.isCalledWithReturn()) { for (SPParameter param : obj.getParameters()) { if (param.getParameterType() == SPParameter.RETURN_VALUE) { if (param.getExpression() == null) { append("?"); //$NON-NLS-1$ } else { visitNode(param.getExpression()); } } } append(SPACE); append(Tokens.EQ); append(SPACE); } // exec clause append(EXEC); append(SPACE); append(obj.getProcedureName()); append("("); //$NON-NLS-1$ boolean first = true; for (SPParameter param : obj.getParameters()) { if (param.isUsingDefault() || param.getParameterType() == SPParameter.RETURN_VALUE || param.getParameterType() == SPParameter.RESULT_SET || param.getExpression() == null) { continue; } if (first) { first = false; } else { append(", "); //$NON-NLS-1$ } if (obj.displayNamedParameters()) { append(escapeSinglePart(Symbol.getShortName(param.getParameterSymbol().getOutputName()))); append(" => "); //$NON-NLS-1$ } boolean addParens = !obj.displayNamedParameters() && param.getExpression() instanceof CompareCriteria; if (addParens) { append(Tokens.LPAREN); } visitNode(param.getExpression()); if (addParens) { append(Tokens.RPAREN); } } append(")"); //$NON-NLS-1$ // Option clause if (obj.getOption() != null) { beginClause(1); visitNode(obj.getOption()); } } public void addCacheHint( CacheHint obj ) { if (obj == null) { return; } append(BEGIN_HINT); append(SPACE); append(CacheHint.CACHE); boolean addParens = false; if (obj.isPrefersMemory()) { append(Tokens.LPAREN); addParens = true; append(CacheHint.PREF_MEM); } if (obj.getTtl() != null) { if (!addParens) { append(Tokens.LPAREN); addParens = true; } else { append(SPACE); } append(CacheHint.TTL); append(obj.getTtl()); } if (obj.getUpdatable() != null) { if (!addParens) { append(Tokens.LPAREN); addParens = true; } else { append(SPACE); } append(CacheHint.UPDATABLE); } if (obj.getScope() != null) { if (!addParens) { append(Tokens.LPAREN); addParens = true; } else { append(SPACE); } append(CacheHint.SCOPE); append(obj.getScope()); } if (obj.getMinRows() != null) { if (!addParens) { append(Tokens.LPAREN); addParens = true; } else { append(SPACE); } append(CacheHint.MIN); append(obj.getMinRows()); } if (addParens) { append(Tokens.RPAREN); } append(SPACE); append(END_HINT); beginClause(0); } @Override public void visit( SubqueryFromClause obj ) { addHintComment(obj); if (obj.isLateral()) { append(LATERAL); } append("(");//$NON-NLS-1$ visitNode(obj.getCommand()); append(")");//$NON-NLS-1$ append(" AS ");//$NON-NLS-1$ append(escapeSinglePart(obj.getOutputName())); } @Override public void visit( SubquerySetCriteria obj ) { // variable visitNode(obj.getExpression()); // operator and beginning of list append(SPACE); if (obj.isNegated()) { append(NOT); append(SPACE); } append(IN); addSubqueryHint(obj.getSubqueryHint()); append(" ("); //$NON-NLS-1$ visitNode(obj.getCommand()); append(")"); //$NON-NLS-1$ } @Override public void visit( UnaryFromClause obj ) { addHintComment(obj); if (obj.getExpandedCommand() != null) { append("(");//$NON-NLS-1$ visitNode(obj.getExpandedCommand()); append(")");//$NON-NLS-1$ append(" AS ");//$NON-NLS-1$ append(escapeSinglePart(obj.getGroup().getName())); } else { visitNode(obj.getGroup()); } } @Override public void visit( Update obj ) { // Update clause append(UPDATE); addSourceHint(obj.getSourceHint()); append(SPACE); visitNode(obj.getGroup()); beginClause(1); // Set clause append(SET); beginClause(2); visitNode(obj.getChangeList()); // Where clause if (obj.getCriteria() != null) { beginClause(1); visitCriteria(WHERE, obj.getCriteria()); } // Option clause if (obj.getOption() != null) { beginClause(1); visitNode(obj.getOption()); } } @Override public void visit( Into obj ) { append(INTO); append(SPACE); visitNode(obj.getGroup()); } // ############ Visitor methods for symbol objects #################### @Override public void visit( AggregateSymbol obj ) { append(obj.getName()); append("("); //$NON-NLS-1$ if (obj.isDistinct()) { append(DISTINCT); append(" "); //$NON-NLS-1$ } else if (obj.getAggregateFunction() == Type.USER_DEFINED) { //TODO: left in to help the parser, but can be removed append(ALL); append(" "); //$NON-NLS-1$ } if (obj.getArgs().length == 0) { if (obj.getAggregateFunction() == Type.COUNT) { append(Tokens.ALL_COLS); } } else { registerNodes(obj.getArgs(), 0); } if (obj.getOrderBy() != null) { append(SPACE); visitNode(obj.getOrderBy()); } append(")"); //$NON-NLS-1$ if (obj.getCondition() != null) { append(SPACE); append(FILTER); append(Tokens.LPAREN); append(WHERE); append(SPACE); append(obj.getCondition()); append(Tokens.RPAREN); } } @Override public void visit( AliasSymbol obj ) { visitNode(obj.getSymbol()); append(SPACE); append(AS); append(SPACE); append(escapeSinglePart(obj.getOutputName())); } @Override public void visit( MultipleElementSymbol obj ) { if (obj.getGroup() == null) { append(Tokens.ALL_COLS); } else { visitNode(obj.getGroup()); append(Tokens.DOT); append(Tokens.ALL_COLS); } } @Override public void visit( Constant obj ) { Class<?> type = obj.getType(); boolean multiValued = obj.isMultiValued(); Object value = obj.getValue(); outputLiteral(type, multiValued, value); } private void outputLiteral(Class<?> type, boolean multiValued, Object value) throws AssertionError { String[] constantParts = null; if (multiValued) { constantParts = new String[] {"?"}; //$NON-NLS-1$ } else if (value == null) { if (type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) { constantParts = new String[] {UNKNOWN}; } else { constantParts = new String[] {"null"}; //$NON-NLS-1$ } } else { if (value.getClass() == ArrayImpl.class) { ArrayImpl av = (ArrayImpl)value; append(Tokens.LPAREN); for (int i = 0; i < av.getValues().length; i++) { if (i > 0) { append(Tokens.COMMA); append(SPACE); } Object value2 = av.getValues()[i]; outputLiteral(value2!=null?value2.getClass():av.getValues().getClass().getComponentType(), multiValued, value2); } if (av.getValues().length == 1) { append(Tokens.COMMA); } append(Tokens.RPAREN); return; } else if (type.isArray()) { append(Tokens.LPAREN); int length = java.lang.reflect.Array.getLength(value); for (int i = 0; i < length; i++) { if (i > 0) { append(Tokens.COMMA); append(SPACE); } Object value2 = java.lang.reflect.Array.get(value, i); outputLiteral(type.getComponentType(), multiValued, value2); } if (length == 1) { append(Tokens.COMMA); } append(Tokens.RPAREN); return; } if (Number.class.isAssignableFrom(type)) { constantParts = new String[] {value.toString()}; } else if (type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) { constantParts = new String[] {value.equals(Boolean.TRUE) ? TRUE : FALSE}; } else if (type.equals(DataTypeManager.DefaultDataClasses.TIMESTAMP)) { constantParts = new String[] {"{ts'", value.toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$ } else if (type.equals(DataTypeManager.DefaultDataClasses.TIME)) { constantParts = new String[] {"{t'", value.toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$ } else if (type.equals(DataTypeManager.DefaultDataClasses.DATE)) { constantParts = new String[] {"{d'", value.toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$ } else if (type.equals(DataTypeManager.DefaultDataClasses.VARBINARY)) { constantParts = new String[] {"X'", value.toString(), "'"}; //$NON-NLS-1$ //$NON-NLS-2$ } if (constantParts == null) { if (DataTypeManager.isLOB(type)) { constantParts = new String[] {"?"}; //$NON-NLS-1$ } else { append('\''); String strValue = value.toString(); for (int i = 0; i < strValue.length(); i++) { char c = strValue.charAt(i); if (c == '\'') { parts.append('\''); } else if (Character.isISOControl(c)) { parts.append("\\u" + PropertiesUtils.toHex((c >> 12) & 0xF) + PropertiesUtils.toHex((c >> 8) & 0xF) //$NON-NLS-1$ + PropertiesUtils.toHex((c >> 4) & 0xF) + PropertiesUtils.toHex(c & 0xF)); continue; } parts.append(c); } parts.append('\''); return; } } } for (String string : constantParts) { append(string); } } /** * Take a string literal and escape it as necessary. By default, this converts ' to ''. * * @param str String literal value (unquoted), never null * @return Escaped string literal value */ static String escapeStringValue( String str, String tick ) { return StringUtil.replaceAll(str, tick, tick + tick); } @Override public void visit( ElementSymbol obj ) { if (obj.getDisplayMode().equals(ElementSymbol.DisplayMode.SHORT_OUTPUT_NAME) || shortNameOnly) { outputShortName(obj); return; } String name = obj.getOutputName(); if (obj.getDisplayMode().equals(ElementSymbol.DisplayMode.FULLY_QUALIFIED)) { name = obj.getName(); } outputDisplayName(name); } private void outputShortName( ElementSymbol obj ) { outputDisplayName(Symbol.getShortName(obj.getOutputName())); } private void outputDisplayName( String name ) { String[] pathParts = name.split("\\."); //$NON-NLS-1$ for (int i = 0; i < pathParts.length; i++) { if (i > 0) { append(Symbol.SEPARATOR); } append(escapeSinglePart(pathParts[i])); } } @Override public void visit( ExpressionSymbol obj ) { visitNode(obj.getExpression()); } @Override public void visit( Function obj ) { String name = obj.getName(); Expression[] args = obj.getArgs(); if (obj.isImplicit()) { // Hide this function, which is implicit visitNode(args[0]); } else if (name.equalsIgnoreCase(CONVERT) || name.equalsIgnoreCase(CAST) || name.equalsIgnoreCase(XMLCAST)) { append(name); append("("); //$NON-NLS-1$ if (args != null && args.length > 0) { visitNode(args[0]); if (name.equalsIgnoreCase(CONVERT)) { append(", "); //$NON-NLS-1$ } else { append(" "); //$NON-NLS-1$ append(AS); append(" "); //$NON-NLS-1$ } if (args.length < 2 || args[1] == null || !(args[1] instanceof Constant)) { append(UNDEFINED); } else { append(((Constant)args[1]).getValue()); } } append(")"); //$NON-NLS-1$ } else if (INFIX_FUNCTIONS.contains(name)) { append("("); //$NON-NLS-1$ if (args != null) { for (int i = 0; i < args.length; i++) { visitNode(args[i]); if (i < (args.length - 1)) { append(SPACE); append(name); append(SPACE); } } } append(")"); //$NON-NLS-1$ } else if (name.equalsIgnoreCase(NonReserved.TIMESTAMPADD) || name.equalsIgnoreCase(NonReserved.TIMESTAMPDIFF)) { append(name); append("("); //$NON-NLS-1$ if (args != null && args.length > 0) { append(((Constant)args[0]).getValue()); registerNodes(args, 1); } append(")"); //$NON-NLS-1$ } else if (name.equalsIgnoreCase(SourceSystemFunctions.XMLPI)) { append(name); append("(NAME "); //$NON-NLS-1$ outputDisplayName((String)((Constant)args[0]).getValue()); registerNodes(args, 1); append(")"); //$NON-NLS-1$ } else if (name.equalsIgnoreCase(SourceSystemFunctions.TRIM)) { append(name); append(SQLConstants.Tokens.LPAREN); String value = (String)((Constant)args[0]).getValue(); if (!value.equalsIgnoreCase(BOTH)) { append(((Constant)args[0]).getValue()); append(" "); //$NON-NLS-1$ } append(args[1]); append(" "); //$NON-NLS-1$ append(FROM); append(" "); //$NON-NLS-1$ append(args[2]); append(")"); //$NON-NLS-1$ } else { append(name); append("("); //$NON-NLS-1$ registerNodes(args, 0); append(")"); //$NON-NLS-1$ } } private void registerNodes( LanguageObject[] objects, int begin ) { registerNodes(Arrays.asList(objects), begin); } private void registerNodes( List<? extends LanguageObject> objects, int begin ) { for (int i = begin; i < objects.size(); i++) { if (i > 0) { append(", "); //$NON-NLS-1$ } visitNode(objects.get(i)); } } @Override public void visit( GroupSymbol obj ) { String alias = null; String fullGroup = obj.getOutputName(); if (obj.getOutputDefinition() != null) { alias = obj.getOutputName(); fullGroup = obj.getOutputDefinition(); } outputDisplayName(fullGroup); if (alias != null) { append(SPACE); append(AS); append(SPACE); append(escapeSinglePart(alias)); } } @Override public void visit( Reference obj ) { if (!obj.isPositional() && obj.getExpression() != null) { visitNode(obj.getExpression()); } else { append("?"); //$NON-NLS-1$ } } // ############ Visitor methods for storedprocedure language objects #################### @Override public void visit( Block obj ) { addLabel(obj); List<Statement> statements = obj.getStatements(); // Add first clause append(BEGIN); if (obj.isAtomic()) { append(SPACE); append(ATOMIC); } append("\n"); //$NON-NLS-1$ addStatements(statements); if (obj.getExceptionGroup() != null) { append(NonReserved.EXCEPTION); append(SPACE); outputDisplayName(obj.getExceptionGroup()); append("\n"); //$NON-NLS-1$ if (obj.getExceptionStatements() != null) { addStatements(obj.getExceptionStatements()); } } append(END); } private void addStatements(List<Statement> statements) { Iterator<Statement> stmtIter = statements.iterator(); while (stmtIter.hasNext()) { // Add each statement addTabs(1); visitNode(stmtIter.next()); append("\n"); //$NON-NLS-1$ } addTabs(0); } private void addLabel(Labeled obj) { if (obj.getLabel() != null) { outputDisplayName(obj.getLabel()); append(SPACE); append(Tokens.COLON); append(SPACE); } } /** * @param level */ protected void addTabs( int level ) { } @Override public void visit( CommandStatement obj ) { visitNode(obj.getCommand()); if (!obj.isReturnable()) { append(SPACE); append(WITHOUT); append(SPACE); append(RETURN); } append(";"); //$NON-NLS-1$ } @Override public void visit( CreateProcedureCommand obj ) { addCacheHint(obj.getCacheHint()); visitNode(obj.getBlock()); } @Override public void visit( DeclareStatement obj ) { append(DECLARE); append(SPACE); append(obj.getVariableType()); append(SPACE); createAssignment(obj); } /** * @param obj * @param parts */ private void createAssignment( AssignmentStatement obj ) { visitNode(obj.getVariable()); if (obj.getExpression() != null) { append(" = "); //$NON-NLS-1$ visitNode(obj.getExpression()); } append(";"); //$NON-NLS-1$ } @Override public void visit( IfStatement obj ) { append(IF); append("("); //$NON-NLS-1$ visitNode(obj.getCondition()); append(")\n"); //$NON-NLS-1$ addTabs(0); visitNode(obj.getIfBlock()); if (obj.hasElseBlock()) { append("\n"); //$NON-NLS-1$ addTabs(0); append(ELSE); append("\n"); //$NON-NLS-1$ addTabs(0); visitNode(obj.getElseBlock()); } } @Override public void visit( AssignmentStatement obj ) { createAssignment(obj); } @Override public void visit( RaiseStatement obj ) { append(NonReserved.RAISE); append(SPACE); if (obj.isWarning()) { append(SQLWARNING); append(SPACE); } visitNode(obj.getExpression()); append(";"); //$NON-NLS-1$ } @Override public void visit(ExceptionExpression exceptionExpression) { append(SQLEXCEPTION); append(SPACE); visitNode(exceptionExpression.getMessage()); if (exceptionExpression.getSqlState() != null) { append(SPACE); append(SQLSTATE); append(SPACE); append(exceptionExpression.getSqlState()); if (exceptionExpression.getErrorCode() != null) { append(Tokens.COMMA); append(SPACE); append(exceptionExpression.getErrorCode()); } } if (exceptionExpression.getParent() != null) { append(SPACE); append(NonReserved.CHAIN); append(SPACE); append(exceptionExpression.getParent()); } } @Override public void visit(ReturnStatement obj) { append(RETURN); if (obj.getExpression() != null) { append(SPACE); visitNode(obj.getExpression()); } append(Tokens.SEMICOLON); } @Override public void visit( BranchingStatement obj ) { switch (obj.getMode()) { case CONTINUE: append(CONTINUE); break; case BREAK: append(BREAK); break; case LEAVE: append(LEAVE); break; } if (obj.getLabel() != null) { append(SPACE); outputDisplayName(obj.getLabel()); } append(";"); //$NON-NLS-1$ } @Override public void visit( LoopStatement obj ) { addLabel(obj); append(LOOP); append(" "); //$NON-NLS-1$ append(ON); append(" ("); //$NON-NLS-1$ visitNode(obj.getCommand()); append(") "); //$NON-NLS-1$ append(AS); append(" "); //$NON-NLS-1$ outputDisplayName(obj.getCursorName()); append("\n"); //$NON-NLS-1$ addTabs(0); visitNode(obj.getBlock()); } @Override public void visit( WhileStatement obj ) { addLabel(obj); append(WHILE); append("("); //$NON-NLS-1$ visitNode(obj.getCondition()); append(")\n"); //$NON-NLS-1$ addTabs(0); visitNode(obj.getBlock()); } @Override public void visit( ExistsCriteria obj ) { if (obj.isNegated()) { append(NOT); append(SPACE); } append(EXISTS); addSubqueryHint(obj.getSubqueryHint()); append(" ("); //$NON-NLS-1$ visitNode(obj.getCommand()); append(")"); //$NON-NLS-1$ } public void addSubqueryHint(SubqueryHint hint) { if (hint.isNoUnnest()) { append(SPACE); append(BEGIN_HINT); append(SPACE); append(SubqueryHint.NOUNNEST); append(SPACE); append(END_HINT); } else if (hint.isDepJoin()) { append(SPACE); append(BEGIN_HINT); append(SPACE); append(SubqueryHint.DJ); append(SPACE); append(END_HINT); } else if (hint.isMergeJoin()) { append(SPACE); append(BEGIN_HINT); append(SPACE); append(SubqueryHint.MJ); append(SPACE); append(END_HINT); } } @Override public void visit( SubqueryCompareCriteria obj ) { Expression leftExpression = obj.getLeftExpression(); visitNode(leftExpression); String operator = obj.getOperatorAsString(); String quantifier = obj.getPredicateQuantifierAsString(); // operator and beginning of list append(SPACE); append(operator); append(SPACE); append(quantifier); addSubqueryHint(obj.getSubqueryHint()); append(" ("); //$NON-NLS-1$ if (obj.getCommand() != null) { visitNode(obj.getCommand()); } else { visitNode(obj.getArrayExpression()); } append(")"); //$NON-NLS-1$ } @Override public void visit( ScalarSubquery obj ) { if (obj.getSubqueryHint().isDepJoin() || obj.getSubqueryHint().isMergeJoin() || obj.getSubqueryHint().isNoUnnest()) { if (this.parts.length() > 0 && this.parts.charAt(this.parts.length()-1) == ' ') { this.parts.setLength(this.parts.length() -1); } addSubqueryHint(obj.getSubqueryHint()); append(SPACE); } append("("); //$NON-NLS-1$ visitNode(obj.getCommand()); append(")"); //$NON-NLS-1$ } @Override public void visit( XMLAttributes obj ) { append(XMLATTRIBUTES); append("("); //$NON-NLS-1$ registerNodes(obj.getArgs(), 0); append(")"); //$NON-NLS-1$ } @Override public void visit( XMLElement obj ) { append(XMLELEMENT); append("(NAME "); //$NON-NLS-1$ outputDisplayName(obj.getName()); if (obj.getNamespaces() != null) { append(", "); //$NON-NLS-1$ visitNode(obj.getNamespaces()); } if (obj.getAttributes() != null) { append(", "); //$NON-NLS-1$ visitNode(obj.getAttributes()); } if (!obj.getContent().isEmpty()) { append(", "); //$NON-NLS-1$ } registerNodes(obj.getContent(), 0); append(")"); //$NON-NLS-1$ } @Override public void visit( XMLForest obj ) { append(XMLFOREST); append("("); //$NON-NLS-1$ if (obj.getNamespaces() != null) { visitNode(obj.getNamespaces()); append(", "); //$NON-NLS-1$ } registerNodes(obj.getArgs(), 0); append(")"); //$NON-NLS-1$ } @Override public void visit( JSONObject obj ) { append(NonReserved.JSONOBJECT); append("("); //$NON-NLS-1$ registerNodes(obj.getArgs(), 0); append(")"); //$NON-NLS-1$ } @Override public void visit( TextLine obj ) { append(FOR); append(SPACE); registerNodes(obj.getExpressions(), 0); if (obj.getDelimiter() != null) { append(SPACE); append(NonReserved.DELIMITER); append(SPACE); visitNode(new Constant(obj.getDelimiter())); } if (obj.getQuote() != null) { append(SPACE); if (obj.getQuote().charValue() == TextLine.NO_QUOTE_CHAR) { append(NO); append(SPACE); append(NonReserved.QUOTE); } else { append(NonReserved.QUOTE); append(SPACE); visitNode(new Constant(obj.getQuote())); } } if (obj.isIncludeHeader()) { append(SPACE); append(NonReserved.HEADER); } if (obj.getEncoding() != null) { append(SPACE); append(NonReserved.ENCODING); append(SPACE); outputDisplayName(obj.getEncoding()); } } @Override public void visit( XMLNamespaces obj ) { append(XMLNAMESPACES); append("("); //$NON-NLS-1$ for (Iterator<NamespaceItem> items = obj.getNamespaceItems().iterator(); items.hasNext();) { NamespaceItem item = items.next(); if (item.getPrefix() == null) { if (item.getUri() == null) { append("NO DEFAULT"); //$NON-NLS-1$ } else { append("DEFAULT "); //$NON-NLS-1$ visitNode(new Constant(item.getUri())); } } else { visitNode(new Constant(item.getUri())); append(" AS "); //$NON-NLS-1$ outputDisplayName(item.getPrefix()); } if (items.hasNext()) { append(", "); //$NON-NLS-1$ } } append(")"); //$NON-NLS-1$ } @Override public void visit( Limit obj ) { if (!obj.isStrict()) { append(BEGIN_HINT); append(SPACE); append(Limit.NON_STRICT); append(SPACE); append(END_HINT); append(SPACE); } if (obj.getRowLimit() == null) { append(OFFSET); append(SPACE); visitNode(obj.getOffset()); append(SPACE); append(ROWS); return; } append(LIMIT); if (obj.getOffset() != null) { append(SPACE); visitNode(obj.getOffset()); append(","); //$NON-NLS-1$ } append(SPACE); visitNode(obj.getRowLimit()); } @Override public void visit( TextTable obj ) { addHintComment(obj); append("TEXTTABLE("); //$NON-NLS-1$ visitNode(obj.getFile()); if (obj.getSelector() != null) { append(SPACE); append(NonReserved.SELECTOR); append(SPACE); outputLiteral(String.class, false, obj.getSelector()); } append(SPACE); append(NonReserved.COLUMNS); boolean noTrim = obj.isNoTrim(); for (Iterator<TextColumn> cols = obj.getColumns().iterator(); cols.hasNext();) { TextColumn col = cols.next(); append(SPACE); outputDisplayName(col.getName()); append(SPACE); if (col.isOrdinal()) { append(FOR); append(SPACE); append(NonReserved.ORDINALITY); } else { if (col.getHeader() != null) { append(NonReserved.HEADER); append(SPACE); outputLiteral(String.class, false, col.getHeader()); append(SPACE); } append(col.getType()); if (col.getWidth() != null) { append(SPACE); append(NonReserved.WIDTH); append(SPACE); append(col.getWidth()); } if (!noTrim && col.isNoTrim()) { append(SPACE); append(NO); append(SPACE); append(NonReserved.TRIM); } if (col.getSelector() != null) { append(SPACE); append(NonReserved.SELECTOR); append(SPACE); outputLiteral(String.class, false, col.getSelector()); append(SPACE); append(col.getPosition()); } } if (cols.hasNext()) { append(","); //$NON-NLS-1$ } } if (!obj.isUsingRowDelimiter()) { append(SPACE); append(NO); append(SPACE); append(ROW); append(SPACE); append(NonReserved.DELIMITER); } else if (obj.getRowDelimiter() != null) { append(SPACE); append(ROW); append(SPACE); append(NonReserved.DELIMITER); append(SPACE); visitNode(new Constant(obj.getRowDelimiter())); } if (obj.getDelimiter() != null) { append(SPACE); append(NonReserved.DELIMITER); append(SPACE); visitNode(new Constant(obj.getDelimiter())); } if (obj.getQuote() != null) { append(SPACE); if (obj.isEscape()) { append(ESCAPE); } else { append(NonReserved.QUOTE); } append(SPACE); visitNode(new Constant(obj.getQuote())); } if (obj.getHeader() != null) { append(SPACE); append(NonReserved.HEADER); if (1 != obj.getHeader()) { append(SPACE); append(obj.getHeader()); } } if (obj.getSkip() != null) { append(SPACE); append(NonReserved.SKIP); append(SPACE); append(obj.getSkip()); } if (noTrim) { append(SPACE); append(NO); append(SPACE); append(NonReserved.TRIM); } append(")");//$NON-NLS-1$ append(SPACE); append(AS); append(SPACE); outputDisplayName(obj.getName()); } @Override public void visit( XMLTable obj ) { addHintComment(obj); append("XMLTABLE("); //$NON-NLS-1$ if (obj.getNamespaces() != null) { visitNode(obj.getNamespaces()); append(","); //$NON-NLS-1$ append(SPACE); } visitNode(new Constant(obj.getXquery())); if (!obj.getPassing().isEmpty()) { append(SPACE); append(NonReserved.PASSING); append(SPACE); registerNodes(obj.getPassing(), 0); } if (!obj.getColumns().isEmpty() && !obj.isUsingDefaultColumn()) { append(SPACE); append(NonReserved.COLUMNS); for (Iterator<XMLColumn> cols = obj.getColumns().iterator(); cols.hasNext();) { XMLColumn col = cols.next(); append(SPACE); outputDisplayName(col.getName()); append(SPACE); if (col.isOrdinal()) { append(FOR); append(SPACE); append(NonReserved.ORDINALITY); } else { append(col.getType()); if (col.getDefaultExpression() != null) { append(SPACE); append(DEFAULT); append(SPACE); visitNode(col.getDefaultExpression()); } if (col.getPath() != null) { append(SPACE); append(NonReserved.PATH); append(SPACE); visitNode(new Constant(col.getPath())); } } if (cols.hasNext()) { append(","); //$NON-NLS-1$ } } } append(")");//$NON-NLS-1$ append(SPACE); append(AS); append(SPACE); outputDisplayName(obj.getName()); } @Override public void visit( ObjectTable obj ) { addHintComment(obj); append("OBJECTTABLE("); //$NON-NLS-1$ if (obj.getScriptingLanguage() != null) { append(LANGUAGE); append(SPACE); visitNode(new Constant(obj.getScriptingLanguage())); append(SPACE); } visitNode(new Constant(obj.getRowScript())); if (!obj.getPassing().isEmpty()) { append(SPACE); append(NonReserved.PASSING); append(SPACE); registerNodes(obj.getPassing(), 0); } append(SPACE); append(NonReserved.COLUMNS); for (Iterator<ObjectColumn> cols = obj.getColumns().iterator(); cols.hasNext();) { ObjectColumn col = cols.next(); append(SPACE); outputDisplayName(col.getName()); append(SPACE); append(col.getType()); append(SPACE); visitNode(new Constant(col.getPath())); if (col.getDefaultExpression() != null) { append(SPACE); append(DEFAULT); append(SPACE); visitNode(col.getDefaultExpression()); } if (cols.hasNext()) { append(","); //$NON-NLS-1$ } } append(")");//$NON-NLS-1$ append(SPACE); append(AS); append(SPACE); outputDisplayName(obj.getName()); } @Override public void visit( XMLQuery obj ) { append("XMLQUERY("); //$NON-NLS-1$ if (obj.getNamespaces() != null) { visitNode(obj.getNamespaces()); append(","); //$NON-NLS-1$ append(SPACE); } visitNode(new Constant(obj.getXquery())); if (!obj.getPassing().isEmpty()) { append(SPACE); append(NonReserved.PASSING); append(SPACE); registerNodes(obj.getPassing(), 0); } if (obj.getEmptyOnEmpty() != null) { append(SPACE); if (obj.getEmptyOnEmpty()) { append(NonReserved.EMPTY); } else { append(NULL); } append(SPACE); append(ON); append(SPACE); append(NonReserved.EMPTY); } append(")");//$NON-NLS-1$ } @Override public void visit(XMLExists exists) { append("XMLEXISTS("); //$NON-NLS-1$ XMLQuery obj = exists.getXmlQuery(); if (obj.getNamespaces() != null) { visitNode(obj.getNamespaces()); append(","); //$NON-NLS-1$ append(SPACE); } visitNode(new Constant(obj.getXquery())); if (!obj.getPassing().isEmpty()) { append(SPACE); append(NonReserved.PASSING); append(SPACE); registerNodes(obj.getPassing(), 0); } append(")");//$NON-NLS-1$ } @Override public void visit(XMLCast xmlcast) { append("XMLCAST("); //$NON-NLS-1$ append(xmlcast.getExpression()); append(Tokens.SPACE); append(AS); append(Tokens.SPACE); append(xmlcast.getTypeName()); append(")");//$NON-NLS-1$ } @Override public void visit( DerivedColumn obj ) { visitNode(obj.getExpression()); if (obj.getAlias() != null) { append(SPACE); append(AS); append(SPACE); outputDisplayName(obj.getAlias()); } } @Override public void visit( XMLSerialize obj ) { append(XMLSERIALIZE); append(Tokens.LPAREN); if (obj.getDocument() != null) { if (obj.getDocument()) { append(NonReserved.DOCUMENT); } else { append(NonReserved.CONTENT); } append(SPACE); } visitNode(obj.getExpression()); if (obj.getTypeString() != null) { append(SPACE); append(AS); append(SPACE); append(obj.getTypeString()); } if (obj.getEncoding() != null) { append(SPACE); append(NonReserved.ENCODING); append(SPACE); append(escapeSinglePart(obj.getEncoding())); } if (obj.getVersion() != null) { append(SPACE); append(NonReserved.VERSION); append(SPACE); append(new Constant(obj.getVersion())); } if (obj.getDeclaration() != null) { append(SPACE); if (obj.getDeclaration()) { append(NonReserved.INCLUDING); } else { append(NonReserved.EXCLUDING); } append(SPACE); append(NonReserved.XMLDECLARATION); } append(Tokens.RPAREN); } @Override public void visit( QueryString obj ) { append(NonReserved.QUERYSTRING); append("("); //$NON-NLS-1$ visitNode(obj.getPath()); if (!obj.getArgs().isEmpty()) { append(","); //$NON-NLS-1$ append(SPACE); registerNodes(obj.getArgs(), 0); } append(")"); //$NON-NLS-1$ } @Override public void visit( XMLParse obj ) { append(XMLPARSE); append(Tokens.LPAREN); if (obj.isDocument()) { append(NonReserved.DOCUMENT); } else { append(NonReserved.CONTENT); } append(SPACE); visitNode(obj.getExpression()); if (obj.isWellFormed()) { append(SPACE); append(NonReserved.WELLFORMED); } append(Tokens.RPAREN); } @Override public void visit( ExpressionCriteria obj ) { visitNode(obj.getExpression()); } @Override public void visit(TriggerAction obj) { append(FOR); append(SPACE); append(EACH); append(SPACE); append(ROW); append("\n"); //$NON-NLS-1$ addTabs(0); visitNode(obj.getBlock()); } @Override public void visit(ArrayTable obj) { addHintComment(obj); append("ARRAYTABLE("); //$NON-NLS-1$ visitNode(obj.getArrayValue()); append(SPACE); append(NonReserved.COLUMNS); for (Iterator<ProjectedColumn> cols = obj.getColumns().iterator(); cols.hasNext();) { ProjectedColumn col = cols.next(); append(SPACE); outputDisplayName(col.getName()); append(SPACE); append(col.getType()); if (cols.hasNext()) { append(","); //$NON-NLS-1$ } } append(")");//$NON-NLS-1$ append(SPACE); append(AS); append(SPACE); outputDisplayName(obj.getName()); } private void addMakeDep(FromClause obj) { MakeDep makeDep = obj.getMakeDep(); if (makeDep != null && !makeDep.isSimple()) { append(SPACE); append(MAKEDEP); appendMakeDepOptions(makeDep); } makeDep = obj.getMakeInd(); if (makeDep != null && !makeDep.isSimple()) { append(SPACE); append(MAKEIND); appendMakeDepOptions(makeDep); } } @Override public void visit(AlterProcedure alterProcedure) { append(ALTER); append(SPACE); append(PROCEDURE); append(SPACE); append(alterProcedure.getTarget()); beginClause(1); append(AS); addCacheHint(alterProcedure.getCacheHint()); append(alterProcedure.getDefinition().getBlock()); } @Override public void visit(AlterTrigger alterTrigger) { if (alterTrigger.isCreate()) { append(CREATE); } else { append(ALTER); } append(SPACE); append(TRIGGER); append(SPACE); if (alterTrigger.getName() != null) { append(escapeSinglePart(alterTrigger.getName())); append(SPACE); } append(ON); append(SPACE); append(alterTrigger.getTarget()); beginClause(0); if (alterTrigger.isAfter()) { append(NonReserved.AFTER); append(SPACE); } else { append(NonReserved.INSTEAD); append(SPACE); append(OF); append(SPACE); } append(alterTrigger.getEvent()); if (alterTrigger.getDefinition() != null) { beginClause(0); append(AS); append("\n"); //$NON-NLS-1$ addTabs(0); append(alterTrigger.getDefinition()); } else { append(SPACE); append(alterTrigger.getEnabled()?NonReserved.ENABLED:NonReserved.DISABLED); } } @Override public void visit(AlterView alterView) { append(ALTER); append(SPACE); append(NonReserved.VIEW); append(SPACE); append(alterView.getTarget()); beginClause(0); append(AS); append("\n"); //$NON-NLS-1$ addTabs(0); append(alterView.getDefinition()); } @Override public void visit(WindowFunction windowFunction) { append(windowFunction.getFunction()); append(SPACE); append(OVER); append(SPACE); append(windowFunction.getWindowSpecification()); } @Override public void visit(WindowSpecification windowSpecification) { append(Tokens.LPAREN); boolean needsSpace = false; if (windowSpecification.getPartition() != null) { append(PARTITION); append(SPACE); append(BY); append(SPACE); registerNodes(windowSpecification.getPartition(), 0); needsSpace = true; } if (windowSpecification.getOrderBy() != null) { if (needsSpace) { append(SPACE); } append(windowSpecification.getOrderBy()); } append(Tokens.RPAREN); } @Override public void visit(Array array) { if (!array.isImplicit()) { append(Tokens.LPAREN); } registerNodes(array.getExpressions(), 0); if (!array.isImplicit()) { if (array.getExpressions().size() == 1) { append(Tokens.COMMA); } append(Tokens.RPAREN); } } @Override public void visit(IsDistinctCriteria isDistinctCriteria) { append(isDistinctCriteria.getLeftRowValue()); append(SPACE); append(IS); append(SPACE); if (isDistinctCriteria.isNegated()) { append(NOT); append(SPACE); } append(DISTINCT); append(SPACE); append(FROM); append(SPACE); append(isDistinctCriteria.getRightRowValue()); } public static String escapeSinglePart( String part ) { if (isReservedWord(part)) { return ID_ESCAPE_CHAR + part + ID_ESCAPE_CHAR; } boolean escape = true; char start = part.charAt(0); if (start == '#' || start == '@' || StringUtil.isLetter(start)) { escape = false; for (int i = 1; !escape && i < part.length(); i++) { char c = part.charAt(i); escape = !StringUtil.isLetterOrDigit(c) && c != '_'; } } if (escape) { return ID_ESCAPE_CHAR + escapeStringValue(part, "\"") + ID_ESCAPE_CHAR; //$NON-NLS-1$ } return part; } /** * Check whether a string is considered a reserved word or not. Subclasses may override to change definition of reserved word. * * @param string String to check * @return True if reserved word */ static boolean isReservedWord( String string ) { if (string == null) { return false; } return SQLConstants.isReservedWord(string); } }