/* * 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.translator.infinispan.hotrod; import static org.teiid.language.SQLConstants.Reserved.HAVING; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.teiid.language.*; import org.teiid.language.Comparison.Operator; import org.teiid.language.Join.JoinType; import org.teiid.language.SQLConstants.Tokens; import org.teiid.language.visitor.SQLStringVisitor; import org.teiid.metadata.Column; import org.teiid.metadata.KeyRecord; import org.teiid.metadata.RuntimeMetadata; import org.teiid.metadata.Table; import org.teiid.translator.TranslatorException; import org.teiid.translator.document.DocumentNode; public class IckleConversionVisitor extends SQLStringVisitor { protected ArrayList<TranslatorException> exceptions = new ArrayList<TranslatorException>(); protected RuntimeMetadata metadata; protected List<Expression> projectedExpressions = new ArrayList<>(); protected NamedTable parentTable; protected NamedTable queriedTable; private Integer rowLimit; private Integer rowOffset; private boolean includePK; protected boolean avoidProjection = false; private DocumentNode rootNode; private DocumentNode joinedNode; private List<String> projectedDocumentAttributes = new ArrayList<>(); private AtomicInteger aliasCounter = new AtomicInteger(); protected boolean nested; public IckleConversionVisitor(RuntimeMetadata metadata, boolean includePK) { this.metadata = metadata; this.includePK = includePK; this.shortNameOnly = true; } public Table getParentTable() { return parentTable.getMetadataObject(); } public NamedTable getParentNamedTable() { return parentTable; } public Table getQueryTable() { return this.queriedTable.getMetadataObject(); } public NamedTable getQueryNamedTable() { return this.queriedTable; } public boolean isNestedOperation() { return this.nested; } @Override public void visit(NamedTable obj) { this.queriedTable = obj; if (obj.getCorrelationName() == null) { obj.setCorrelationName(obj.getMetadataObject().getName().toLowerCase()+"_"+aliasCounter.getAndIncrement()); } if (this.rootNode == null) { String messageName = null; String aliasName = null; String mergedTableName = ProtobufMetadataProcessor.getMerge(obj.getMetadataObject()); if (mergedTableName == null) { aliasName = obj.getCorrelationName(); messageName = getMessageName(obj.getMetadataObject()); this.parentTable = obj; this.rootNode = new DocumentNode(obj.getMetadataObject(), true); this.joinedNode = this.rootNode; // check to see if there is one-2-one rows Set<String> tags = new HashSet<>(); for (Column column:obj.getMetadataObject().getColumns()) { if (ProtobufMetadataProcessor.getParentTag(column) != -1) { String childMessageName = ProtobufMetadataProcessor.getMessageName(column); if (!tags.contains(childMessageName)) { tags.add(childMessageName); //TODO: DocumentNode needs to be refactored to just take name, not table Table t = new Table(); t.setName(childMessageName); this.joinedNode = this.rootNode.joinWith(JoinType.INNER_JOIN, new DocumentNode(t, false)); } } } } else { try { Table mergedTable = this.metadata.getTable(mergedTableName); messageName = getMessageName(mergedTable); aliasName = mergedTable.getName().toLowerCase()+"_"+aliasCounter.getAndIncrement(); this.parentTable = new NamedTable(mergedTable.getName(), aliasName, mergedTable); this.rootNode = new DocumentNode(mergedTable, true); this.joinedNode = this.rootNode.joinWith(JoinType.INNER_JOIN, new DocumentNode(obj.getMetadataObject(), true)); this.nested = true; } catch (TranslatorException e) { this.exceptions.add(e); } } buffer.append(messageName); if (aliasName != null) { buffer.append(Tokens.SPACE); buffer.append(aliasName); } if (this.includePK) { KeyRecord pk = this.parentTable.getMetadataObject().getPrimaryKey(); if (pk != null) { for (Column column : pk.getColumns()) { projectedExpressions.add(new ColumnReference(obj, column.getName(), column, column.getJavaType())); } } } } } private String getMessageName(Table obj) { String messageName; messageName = ProtobufMetadataProcessor.getMessageName(obj); if (messageName == null) { messageName = obj.getName(); } return messageName; } public boolean isPartOfPrimaryKey(String columnName) { KeyRecord pk = getParentTable().getPrimaryKey(); if (pk != null) { for (Column column:pk.getColumns()) { if (column.getName().equals(columnName)) { return true; } } } return false; } @Override public void visit(Join obj) { Condition cond = null; if (obj.getLeftItem() instanceof Join) { cond = obj.getCondition(); append(obj.getLeftItem()); Table right = ((NamedTable)obj.getRightItem()).getMetadataObject(); this.joinedNode.joinWith(obj.getJoinType(), new DocumentNode(right, true)); } else if (obj.getRightItem() instanceof Join) { cond = obj.getCondition(); append(obj.getRightItem()); Table left = ((NamedTable)obj.getLeftItem()).getMetadataObject(); this.joinedNode.joinWith(obj.getJoinType(), new DocumentNode(left, true)); } else { cond = obj.getCondition(); append(obj.getLeftItem()); this.queriedTable = (NamedTable)obj.getRightItem(); Table right = ((NamedTable)obj.getRightItem()).getMetadataObject(); this.joinedNode.joinWith(obj.getJoinType(), new DocumentNode(right, true)); } if (cond != null) { append(cond); } } @Override public void visit(Limit obj) { if (obj.getRowOffset() != 0) { this.rowOffset = new Integer(obj.getRowOffset()); } if (obj.getRowLimit() != 0) { this.rowLimit = new Integer(obj.getRowLimit()); } } @Override public void visit(Select obj) { buffer.append(SQLConstants.Reserved.FROM).append(Tokens.SPACE); visitNodes(obj.getFrom()); if (obj.getWhere() != null) { buffer.append(Tokens.SPACE); buffer.append(SQLConstants.Reserved.WHERE).append(Tokens.SPACE); visitNode(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); visitNode(obj.getOrderBy()); if (this.nested) { this.exceptions.add(new TranslatorException(InfinispanPlugin.Util.gs(InfinispanPlugin.Event.TEIID25010))); } } if (obj.getLimit() != null) { buffer.append(Tokens.SPACE); visitNode(obj.getLimit()); } visitNodes(obj.getDerivedColumns()); } @Override public void visit(Comparison obj) { if (obj.getOperator() == Operator.EQ && obj.getLeftExpression() instanceof ColumnReference && obj.getRightExpression() instanceof ColumnReference) { // this typically is join. Column left = ((ColumnReference)obj.getLeftExpression()).getMetadataObject(); Column right = ((ColumnReference)obj.getRightExpression()).getMetadataObject(); if (getQualifiedName(left).equals(getQualifiedName(right))) { return; } } super.visit(obj); } @Override public void visit(ColumnReference obj) { buffer.append(getQualifiedName(obj.getMetadataObject())); } @Override public void visit(DerivedColumn obj) { if (obj.getExpression() instanceof ColumnReference) { Column column = ((ColumnReference)obj.getExpression()).getMetadataObject(); if (!column.isSelectable()) { this.exceptions.add(new TranslatorException(InfinispanPlugin.Util .gs(InfinispanPlugin.Event.TEIID25001, column.getName()))); } column = normalizePseudoColumn(column); if (!this.includePK || !isPartOfPrimaryKey(column.getName())) { if (column.getParent().equals(this.parentTable.getMetadataObject())){ this.projectedExpressions.add(new ColumnReference(this.parentTable, column.getName(), column, column.getJavaType())); } else { this.projectedExpressions.add(new ColumnReference(this.queriedTable, column.getName(), column, column.getJavaType())); } } boolean nested = false; if (ProtobufMetadataProcessor.getParentTag(column) != -1 || ProtobufMetadataProcessor.getParentTag((Table) column.getParent()) != -1) { this.avoidProjection = true; nested = true; } try { this.projectedDocumentAttributes.add(MarshallerBuilder.getDocumentAttributeName(column, nested, this.metadata)); } catch (TranslatorException e) { this.exceptions.add(e); } } else if (obj.getExpression() instanceof Function) { if (!this.parentTable.equals(this.queriedTable)) { this.exceptions.add(new TranslatorException(InfinispanPlugin.Event.TEIID25008, InfinispanPlugin.Util.gs(InfinispanPlugin.Event.TEIID25008))); } AggregateFunction func = (AggregateFunction)obj.getExpression(); this.projectedExpressions.add(func); // Aggregate functions can not be part of the implicit query projection when the complex object is involved // thus not adding to projectedDocumentAttributes. i.e. sum(g2.g3.e1) is not supported by Infinispan AFAIK. } else { this.exceptions.add(new TranslatorException(InfinispanPlugin.Util.gs(InfinispanPlugin.Event.TEIID25002, obj))); } } Column normalizePseudoColumn(Column column) { String pseudo = ProtobufMetadataProcessor.getPseudo(column); if (pseudo != null) { try { Table columnParent = (Table)column.getParent(); Table pseudoColumnParent = this.metadata.getTable( ProtobufMetadataProcessor.getMerge(columnParent)); return pseudoColumnParent.getColumnByName(getName(column)); } catch (TranslatorException e) { this.exceptions.add(e); } } return column; } public String getQuery() { StringBuilder sb = new StringBuilder(); if (!this.avoidProjection) { addSelectedColumns(sb); sb.append(Tokens.SPACE); } sb.append(super.toString()); return sb.toString(); } String getQualifiedName(Column column) { String aliasName = this.parentTable.getCorrelationName(); String nis = getName(column); String parentName = ProtobufMetadataProcessor.getParentColumnName(column); if (parentName == null && !ProtobufMetadataProcessor.isPseudo(column)) { parentName = ProtobufMetadataProcessor.getParentColumnName((Table)column.getParent()); } if (parentName != null) { nis = parentName + Tokens.DOT + nis; } if (aliasName != null) { return aliasName + Tokens.DOT + nis; } return nis; } StringBuilder addSelectedColumns(StringBuilder sb) { sb.append(SQLConstants.Reserved.SELECT).append(Tokens.SPACE); boolean first = true; for (Expression expr : this.projectedExpressions) { if (!first) { sb.append(Tokens.COMMA).append(Tokens.SPACE); } if (expr instanceof ColumnReference) { Column column = ((ColumnReference) expr).getMetadataObject(); String nis = getQualifiedName(column); sb.append(nis); } else if (expr instanceof Function) { Function func = (Function) expr; sb.append(func.getName()).append(Tokens.LPAREN); if (func.getParameters().isEmpty() && SQLConstants.NonReserved.COUNT.equalsIgnoreCase(func.getName())) { sb.append(Tokens.ALL_COLS); } else { ColumnReference columnRef = (ColumnReference) func.getParameters().get(0); Column column = columnRef.getMetadataObject(); String nis = getQualifiedName(column); sb.append(nis); } sb.append(Tokens.RPAREN); } first = false; } return sb; } public Integer getRowLimit() { return rowLimit; } public Integer getRowOffset() { return rowOffset; } @Override protected boolean useAsInGroupAlias(){ return false; } public List<String> getProjectedDocumentAttributes() throws TranslatorException { return projectedDocumentAttributes; } RuntimeMetadata getMetadata() { return metadata; } DocumentNode getDocumentNode() { return this.rootNode; } }