/* * 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.dqp.internal.process; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeSet; import org.teiid.adminapi.impl.VDBMetaData; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.metadata.AbstractMetadataRecord; import org.teiid.query.eval.Evaluator; import org.teiid.query.metadata.CompositeMetadataStore; import org.teiid.query.metadata.TransformationMetadata; import org.teiid.query.processor.relational.RelationalNode; import org.teiid.query.sql.lang.Criteria; import org.teiid.query.sql.lang.OrderBy; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.Function; import org.teiid.query.tempdata.BaseIndexInfo; import org.teiid.query.tempdata.SearchableTable; import org.teiid.query.util.CommandContext; abstract class RecordTable<T extends AbstractMetadataRecord> implements SearchableTable { public interface SimpleIterator<T> { public T next() throws TeiidProcessingException, TeiidComponentException; } private static final SimpleIterator<?> empty = new SimpleIterator<Object>() { @Override public Object next() throws TeiidProcessingException, TeiidComponentException { return null; } }; @SuppressWarnings("unchecked") public static <T> SimpleIterator<T> emptyIterator() { return (SimpleIterator<T>) empty; } public static class SimpleIteratorWrapper<T> implements SimpleIterator<T> { private Iterator<? extends T> iter; public SimpleIteratorWrapper(Iterator<? extends T> iter) { this.iter = iter; } public void setIterator(Iterator<? extends T> iter) { this.iter = iter; } @Override public T next() throws TeiidProcessingException, TeiidComponentException { while (iter.hasNext()) { T result = iter.next(); if (result != null && isValid(result)) { return result; } } return null; } protected boolean isValid(T result) { return true; } } public static abstract class ExpandingSimpleIterator<P, T> implements SimpleIterator<T> { private SimpleIterator<T> childIter; private SimpleIterator<P> parentIter; private P currentParent; public ExpandingSimpleIterator(SimpleIterator<P> parentIter) { this.parentIter = parentIter; } @Override public T next() throws TeiidProcessingException, TeiidComponentException { while (true) { if (childIter == null) { currentParent = parentIter.next(); if (currentParent == null) { return null; } childIter = getChildIterator(currentParent); } T t = childIter.next(); if (t != null) { return t; } childIter = null; } } public P getCurrentParent() { return currentParent; } protected abstract SimpleIterator<T> getChildIterator(P parent); } private Map<Expression, Integer> columnMap; private Expression[] pkColumns; protected Evaluator eval; public RecordTable(int[] pkColumnIndexs, List<ElementSymbol> columns) { this.columnMap = RelationalNode.createLookupMap(columns); this.pkColumns = new ElementSymbol[pkColumnIndexs.length]; for (int i = 0; i < pkColumnIndexs.length; i++) { pkColumns[i] = columns.get(pkColumnIndexs[i]); } eval = new Evaluator(columnMap, null, null); } @Override public int getPkLength() { return pkColumns.length; } @Override public Map<Expression, Integer> getColumnMap() { return columnMap; } @Override public Boolean matchesPkColumn(int pkIndex, Expression ex) { if (ex instanceof Function) { Function f = (Function)ex; ex = f.getArg(0); } return (pkColumns[pkIndex].equals(ex)); } @Override public boolean supportsOrdering(int pkIndex, Expression ex) { return !(ex instanceof ElementSymbol); } public abstract SimpleIterator<T> processQuery(final VDBMetaData vdb, CompositeMetadataStore metadataStore, BaseIndexInfo<?> ii, TransformationMetadata metadata, CommandContext commandContext); public SimpleIterator<T> processQuery(final VDBMetaData vdb, NavigableMap<String, ?> map, BaseIndexInfo<?> ii, final CommandContext commandContext) { final Criteria crit = ii.getCoveredCriteria(); final ArrayList<Object> rowBuffer = new ArrayList<Object>(1); if (!ii.getValueSet().isEmpty()) { final List<List<Object>> vals = ii.getValueSet(); final SortedMap<String, ?> fMap = map; return new SimpleIterator<T>() { int i = 0; TreeSet<String> seen = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); @Override public T next() throws TeiidProcessingException, TeiidComponentException { while (i < vals.size()) { String key = (String)vals.get(i++).get(0); if (!seen.add(key)) { //filter to only a single match continue; } T s = extractRecord(fMap.get(key)); if (isValid(s, vdb, rowBuffer, crit, commandContext)) { return s; } } return null; } }; } try { if (ii.getLower() != null) { if (ii.getUpper() != null) { map = map.subMap((String) ii.getLower().get(0), true, (String) ii.getUpper().get(0), true); } else { map = map.tailMap((String) ii.getLower().get(0), true); } } else if (ii.getUpper() != null) { map = map.headMap((String) ii.getUpper().get(0), true); } final Iterator<?> iter = map.values().iterator(); return new SimpleIterator<T>() { @Override public T next() throws TeiidProcessingException, TeiidComponentException { while (iter.hasNext()) { T s = extractRecord(iter.next()); if (isValid(s, vdb, rowBuffer, crit, commandContext)) { return s; } } return null; } }; } catch (IllegalArgumentException e) { //this is a map bound issue or lower is greater than upper return emptyIterator(); } } protected T extractRecord(Object val) { return (T) val; } public BaseIndexInfo<RecordTable<?>> planQuery(Query query, Criteria condition, CommandContext context) { BaseIndexInfo<RecordTable<?>> info = new BaseIndexInfo<RecordTable<?>>(this, Collections.EMPTY_LIST, condition, null, false); if (!info.getValueSet().isEmpty()) { info.sortValueSet(OrderBy.ASC, context.getBufferManager().getOptions().getDefaultNullOrder()); } return info; } /** * * @param s * @param vdb * @param rowBuffer * @param condition * @param commandContext * @return * @throws TeiidProcessingException * @throws TeiidComponentException */ protected boolean isValid(T s, VDBMetaData vdb, List<Object> rowBuffer, Criteria condition, CommandContext commandContext) throws TeiidProcessingException, TeiidComponentException { if (s == null) { return false; } if (!commandContext.getDQPWorkContext().isAdmin() && !commandContext.getAuthorizationValidator().isAccessible(s, commandContext)) { return false; } if (condition != null) { rowBuffer.clear(); fillRow(s, rowBuffer); return eval.evaluate(condition, rowBuffer); } return true; } protected void fillRow(T s, List<Object> rowBuffer) { rowBuffer.add(s.getName()); } }