use of org.teiid.query.sql.lang.Criteria in project teiid by teiid.
the class ColumnMaskingHelper method maskColumn.
private static Expression maskColumn(ElementSymbol col, GroupSymbol unaliased, QueryMetadataInterface metadata, ExpressionMappingVisitor emv, Map<String, DataPolicy> policies, CommandContext cc) throws TeiidComponentException, TeiidProcessingException {
Object metadataID = col.getMetadataID();
String fullName = metadata.getFullName(metadataID);
final GroupSymbol group = col.getGroupSymbol();
String elementType = metadata.getElementRuntimeTypeName(col.getMetadataID());
Class<?> expectedType = DataTypeManager.getDataTypeClass(elementType);
List<WhenThen> cases = null;
Collection<GroupSymbol> groups = Arrays.asList(unaliased);
for (Map.Entry<String, DataPolicy> entry : policies.entrySet()) {
DataPolicyMetadata dpm = (DataPolicyMetadata) entry.getValue();
PermissionMetaData pmd = dpm.getPermissionMap().get(fullName);
if (pmd == null) {
continue;
}
String maskString = pmd.getMask();
if (maskString == null) {
continue;
}
Criteria condition = null;
if (pmd.getCondition() != null) {
condition = RowBasedSecurityHelper.resolveCondition(metadata, group, metadata.getFullName(group.getMetadataID()), entry, pmd, pmd.getCondition());
} else {
condition = QueryRewriter.TRUE_CRITERIA;
}
Expression mask = (Expression) pmd.getResolvedMask();
if (mask == null) {
try {
mask = QueryParser.getQueryParser().parseExpression(pmd.getMask());
for (SubqueryContainer container : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(mask)) {
container.getCommand().pushNewResolvingContext(groups);
QueryResolver.resolveCommand(container.getCommand(), metadata, false);
}
ResolverVisitor.resolveLanguageObject(mask, groups, metadata);
ValidatorReport report = Validator.validate(mask, metadata, new ValidationVisitor());
if (report.hasItems()) {
ValidatorFailure firstFailure = report.getItems().iterator().next();
// $NON-NLS-1$
throw new QueryMetadataException(QueryPlugin.Event.TEIID31139, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31139, dpm.getName(), fullName) + " " + firstFailure);
}
if (mask.getType() != expectedType) {
mask = ResolverUtil.convertExpression(mask, elementType, metadata);
}
pmd.setResolvedMask(mask.clone());
if (!dpm.isAnyAuthenticated()) {
// we treat this as user deterministic since the data roles won't change. this may change if the logic becomes dynamic
// TODO: this condition may not even be used
cc.setDeterminismLevel(Determinism.USER_DETERMINISTIC);
}
} catch (QueryMetadataException e) {
throw e;
} catch (TeiidException e) {
throw new QueryMetadataException(QueryPlugin.Event.TEIID31129, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31129, dpm.getName(), fullName));
}
} else {
mask = (Expression) mask.clone();
}
if (group.getDefinition() != null) {
PreOrPostOrderNavigator.doVisit(mask, emv, PreOrPostOrderNavigator.PRE_ORDER, true);
}
if (cases == null) {
cases = new ArrayList<ColumnMaskingHelper.WhenThen>();
}
cases.add(new WhenThen(pmd.getOrder(), condition, mask));
}
if (cases == null) {
return col;
}
Collections.sort(cases);
List<Criteria> whens = new ArrayList<Criteria>();
List<Expression> thens = new ArrayList<Expression>();
for (WhenThen whenThen : cases) {
whens.add(whenThen.when);
thens.add(whenThen.then);
}
SearchedCaseExpression sce = new SearchedCaseExpression(whens, thens);
sce.setElseExpression(col);
sce.setType(expectedType);
Expression mask = QueryRewriter.rewriteExpression(sce, cc, metadata, true);
return mask;
}
use of org.teiid.query.sql.lang.Criteria in project teiid by teiid.
the class RecordTable method processQuery.
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();
}
}
use of org.teiid.query.sql.lang.Criteria in project teiid by teiid.
the class RulePlanJoins method groupJoinsForPushing.
/**
* This is a heuristic that checks for joins that may be pushed so they can be removed
* before considering the joins that must be evaluated in MetaMatrix.
*
* By running this, we eliminate the need for running RuleRaiseAccess during join ordering
*
* @param metadata
* @param joinRegion
* @throws QueryMetadataException
* @throws TeiidComponentException
* @throws QueryPlannerException
*/
private void groupJoinsForPushing(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, JoinRegion joinRegion, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
// TODO: consider moving select criteria if it is preventing a join from being pushed down
// TODO: make the criteria checks based upon a guess at selectivity
Map accessMap = getAccessMap(metadata, capFinder, joinRegion);
boolean structureChanged = false;
// search for combinations of join sources that should be pushed down
for (Iterator accessNodeIter = accessMap.entrySet().iterator(); accessNodeIter.hasNext(); ) {
Map.Entry entry = (Map.Entry) accessNodeIter.next();
List<PlanNode> accessNodes = (List) entry.getValue();
if (accessNodes.size() < 2) {
continue;
}
int secondPass = -1;
for (int i = accessNodes.size() - 1; i >= 0; i--) {
PlanNode accessNode1 = accessNodes.get(i);
Object modelId = RuleRaiseAccess.getModelIDFromAccess(accessNode1, metadata);
SupportedJoinCriteria sjc = CapabilitiesUtil.getSupportedJoinCriteria(modelId, metadata, capFinder);
int discoveredJoin = -1;
for (int k = (secondPass == -1 ? accessNodes.size() - 1 : secondPass); k >= 0; k--) {
if (k == i) {
continue;
}
PlanNode accessNode2 = accessNodes.get(k);
List<PlanNode> criteriaNodes = joinRegion.getCriteriaNodes();
List<PlanNode> joinCriteriaNodes = new LinkedList<PlanNode>();
/* hasJoinCriteria will be true if
* 1. there is criteria between accessNode1 and accessNode2 exclusively
* 2. there is criteria between some other source (not the same logical connector) and accessNode1 or accessNode2
*
* Ideally we should be a little smarter in case 2
* - pushing down a same source cross join can be done if we know that a dependent join will be performed
*/
boolean hasJoinCriteria = false;
LinkedList<Criteria> joinCriteria = new LinkedList<Criteria>();
for (PlanNode critNode : criteriaNodes) {
Set<PlanNode> sources = joinRegion.getCritieriaToSourceMap().get(critNode);
if (sources == null) {
continue;
}
if (sources.contains(accessNode1)) {
if (sources.contains(accessNode2) && sources.size() == 2) {
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (RuleRaiseAccess.isSupportedJoinCriteria(sjc, crit, modelId, metadata, capFinder, null)) {
joinCriteriaNodes.add(critNode);
joinCriteria.add(crit);
}
} else if (!accessNodes.containsAll(sources)) {
hasJoinCriteria = true;
}
} else if (sources.contains(accessNode2) && !accessNodes.containsAll(sources)) {
hasJoinCriteria = true;
}
}
/*
* If we failed to find direct criteria, a cross join may still be acceptable
*/
if (joinCriteriaNodes.isEmpty() && (hasJoinCriteria || !canPushCrossJoin(metadata, accessNode1, accessNode2))) {
continue;
}
List<PlanNode> toTest = Arrays.asList(accessNode1, accessNode2);
JoinType joinType = joinCriteria.isEmpty() ? JoinType.JOIN_CROSS : JoinType.JOIN_INNER;
/*
* We need to limit the heuristic grouping as we don't want to create larger source queries than necessary
*/
boolean shouldPush = true;
int sourceCount = NodeEditor.findAllNodes(accessNode1, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
sourceCount += NodeEditor.findAllNodes(accessNode2, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
if (!context.getOptions().isAggressiveJoinGrouping() && accessMap.size() > 1 && joinType == JoinType.JOIN_INNER && (sourceCount > 2 && (accessNode1.hasProperty(Info.MAKE_DEP) || accessNode2.hasProperty(Info.MAKE_DEP)) || sourceCount > 3) && !canPushCrossJoin(metadata, accessNode1, accessNode2)) {
Collection<GroupSymbol> leftGroups = accessNode1.getGroups();
Collection<GroupSymbol> rightGroups = accessNode2.getGroups();
List<Expression> leftExpressions = new ArrayList<Expression>();
List<Expression> rightExpressions = new ArrayList<Expression>();
List<Criteria> nonEquiJoinCriteria = new ArrayList<Criteria>();
RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, joinCriteria, nonEquiJoinCriteria);
// allow a 1-1 join
if (!NewCalculateCostUtil.usesKey(accessNode1, leftExpressions, metadata) || !NewCalculateCostUtil.usesKey(accessNode2, rightExpressions, metadata)) {
// don't push heuristically
shouldPush = false;
}
}
// try to push to the source
if (!shouldPush || RuleRaiseAccess.canRaiseOverJoin(toTest, metadata, capFinder, joinCriteria, joinType, null, context, secondPass != -1, false) == null) {
if (secondPass == -1 && sjc != SupportedJoinCriteria.KEY && discoveredJoin == -1) {
for (Criteria criteria : joinCriteria) {
if (criteria instanceof CompareCriteria && ((CompareCriteria) criteria).isOptional()) {
discoveredJoin = k;
}
}
}
continue;
}
secondPass = -1;
discoveredJoin = -1;
structureChanged = true;
// remove the information that is no longer relevant to the join region
joinRegion.getCritieriaToSourceMap().keySet().removeAll(joinCriteriaNodes);
joinRegion.getCriteriaNodes().removeAll(joinCriteriaNodes);
joinRegion.getJoinSourceNodes().remove(accessNode1);
joinRegion.getJoinSourceNodes().remove(accessNode2);
accessNodes.remove(i);
accessNodes.remove(k < i ? k : k - 1);
// build a new join node
PlanNode joinNode = createJoinNode(accessNode1, accessNode2, joinCriteria, joinType);
PlanNode newAccess = RuleRaiseAccess.raiseAccessOverJoin(joinNode, joinNode.getFirstChild(), entry.getKey(), capFinder, metadata, false);
for (PlanNode critNode : joinCriteriaNodes) {
critNode.removeFromParent();
critNode.removeAllChildren();
}
for (Set<PlanNode> source : joinRegion.getCritieriaToSourceMap().values()) {
if (source.remove(accessNode1) || source.remove(accessNode2)) {
source.add(newAccess);
}
}
joinRegion.getJoinSourceNodes().put(newAccess, newAccess);
accessNodes.add(newAccess);
i = accessNodes.size();
k = accessNodes.size();
break;
}
if (discoveredJoin != -1) {
// rerun with the discoveredJoin criteria
i++;
secondPass = discoveredJoin;
}
}
}
if (structureChanged) {
joinRegion.reconstructJoinRegoin();
}
}
use of org.teiid.query.sql.lang.Criteria in project teiid by teiid.
the class RulePlanOuterJoins method planLeftOuterJoinAssociativityBeforePlanning.
/**
* Similar to {@link #planLeftOuterJoinAssociativity(PlanNode, QueryMetadataInterface, CapabilitiesFinder, AnalysisRecord, CommandContext)},
* but only looks for the creation of inner joins
* @param plan
* @param metadata
* @return
* @throws TeiidComponentException
* @throws QueryMetadataException
*/
private boolean planLeftOuterJoinAssociativityBeforePlanning(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryMetadataException, TeiidComponentException {
boolean changedAny = false;
LinkedHashSet<PlanNode> joins = new LinkedHashSet<PlanNode>(NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN, NodeConstants.Types.ACCESS));
while (!joins.isEmpty()) {
Iterator<PlanNode> i = joins.iterator();
PlanNode join = i.next();
i.remove();
if (join.hasBooleanProperty(Info.PRESERVE)) {
continue;
}
// check for left outer join ordering, such that we can combine for pushdown
if (checkLeftOrdering(metadata, capabilitiesFinder, analysisRecord, context, join)) {
continue;
}
if (!join.getProperty(Info.JOIN_TYPE).equals(JoinType.JOIN_LEFT_OUTER)) {
continue;
}
PlanNode childJoin = null;
PlanNode other = null;
PlanNode left = join.getFirstChild();
PlanNode right = join.getLastChild();
if (left.getType() == NodeConstants.Types.JOIN && left.getProperty(Info.JOIN_TYPE) == JoinType.JOIN_LEFT_OUTER) {
childJoin = left;
other = right;
} else {
continue;
}
List<Criteria> joinCriteria = (List<Criteria>) join.getProperty(Info.JOIN_CRITERIA);
if (!isCriteriaValid(joinCriteria, metadata, join)) {
continue;
}
List<Criteria> childJoinCriteria = (List<Criteria>) childJoin.getProperty(Info.JOIN_CRITERIA);
if (!isCriteriaValid(childJoinCriteria, metadata, childJoin) || join.hasBooleanProperty(Info.PRESERVE)) {
continue;
}
// there are 1 form we can take
// (a b) c -> a (b c)
Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(joinCriteria);
if (Collections.disjoint(groups, FrameUtil.findJoinSourceNode(childJoin.getFirstChild()).getGroups())) {
// rearrange
PlanNode newParent = RulePlanJoins.createJoinNode();
newParent.setProperty(Info.JOIN_TYPE, JoinType.JOIN_LEFT_OUTER);
PlanNode newChild = RulePlanJoins.createJoinNode();
newChild.setProperty(Info.JOIN_TYPE, JoinType.JOIN_LEFT_OUTER);
joins.remove(childJoin);
// a (b c)
newChild.addFirstChild(childJoin.getLastChild());
newChild.addLastChild(other);
newChild.setProperty(Info.JOIN_CRITERIA, joinCriteria);
newParent.addFirstChild(childJoin.getFirstChild());
newParent.addLastChild(newChild);
newParent.setProperty(Info.JOIN_CRITERIA, childJoinCriteria);
updateGroups(newChild);
updateGroups(newParent);
join.getParent().replaceChild(join, newParent);
changedAny = true;
}
}
return changedAny;
}
use of org.teiid.query.sql.lang.Criteria in project teiid by teiid.
the class RulePlanProcedures method findInputNodes.
private void findInputNodes(final HashSet<ElementSymbol> inputs, PlanNode critNode, final List<Criteria> conjuncts, final Set<ElementSymbol> params) {
while (critNode.getType() == NodeConstants.Types.SELECT) {
final PlanNode currentNode = critNode;
final Criteria crit = (Criteria) currentNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
critNode = currentNode.getParent();
if (!currentNode.getGroups().isEmpty()) {
continue;
}
LanguageVisitor visitor = new LanguageVisitor() {
public void visit(CompareCriteria compCrit) {
if (compCrit.getOperator() == CompareCriteria.EQ && checkForInput(compCrit.getLeftExpression()) && !checkForAnyInput(compCrit.getRightExpression())) {
addInputNode((Reference) compCrit.getLeftExpression());
}
}
private void addInputNode(Reference param) {
params.add(param.getExpression());
conjuncts.add(crit);
NodeEditor.removeChildNode(currentNode.getParent(), currentNode);
}
public void visit(IsNullCriteria isNull) {
if (!isNull.isNegated() && checkForInput(isNull.getExpression())) {
addInputNode((Reference) isNull.getExpression());
}
}
public void visit(SetCriteria obj) {
if (!obj.isNegated() && checkForInput(obj.getExpression()) && !checkForAnyInput(obj.getValues())) {
addInputNode((Reference) obj.getExpression());
}
}
public void visit(DependentSetCriteria obj) {
if (obj.isNegated()) {
// just a sanity check
return;
}
if (obj.hasMultipleAttributes()) {
for (AttributeComparison comp : obj.getAttributes()) {
if (!checkForInput(comp.dep)) {
return;
}
}
for (AttributeComparison comp : obj.getAttributes()) {
params.add(((Reference) comp.dep).getExpression());
}
conjuncts.add(crit);
NodeEditor.removeChildNode(currentNode.getParent(), currentNode);
} else if (checkForInput(obj.getExpression())) {
addInputNode((Reference) obj.getExpression());
}
}
boolean checkForInput(Expression expr) {
if (!(expr instanceof Reference)) {
return false;
}
// if the expr is a function containing a reference should give a warning
Reference ref = (Reference) expr;
return inputs.contains(ref.getExpression());
}
boolean checkForAnyInput(LanguageObject expr) {
for (Reference ref : ReferenceCollectorVisitor.getReferences(expr)) {
if (checkForInput(ref)) {
return true;
}
}
return false;
}
boolean checkForAnyInput(Collection<Expression> expressions) {
for (Expression expr : expressions) {
if (checkForAnyInput(expr)) {
return true;
}
}
return false;
}
};
for (Criteria conjunct : Criteria.separateCriteriaByAnd(crit)) {
conjunct.acceptVisitor(visitor);
}
}
}
Aggregations