use of org.teiid.query.sql.lang.SetQuery.Operation in project teiid by teiid.
the class NewCalculateCostUtil method setColStatEstimates.
private static void setColStatEstimates(PlanNode node, float cardinality, QueryMetadataInterface metadata, float leftPercent, float rightPercent) throws QueryMetadataException, TeiidComponentException {
if (cardinality == UNKNOWN_VALUE) {
return;
}
ColStats colStats = null;
ColStats colStatsOther = null;
float childCardinality = UNKNOWN_VALUE;
if (node.getChildCount() > 0) {
childCardinality = node.getFirstChild().getCardinality();
colStats = (ColStats) node.getFirstChild().getProperty(Info.EST_COL_STATS);
}
float otherChildCardinality = UNKNOWN_VALUE;
List<? extends Expression> outputColsOther = null;
if (node.getChildCount() > 1) {
otherChildCardinality = node.getLastChild().getCardinality();
colStatsOther = (ColStats) node.getLastChild().getProperty(Info.EST_COL_STATS);
outputColsOther = getOutputCols(node.getLastChild(), metadata);
}
SetQuery.Operation setOp = (Operation) node.getProperty(Info.SET_OPERATION);
List<? extends Expression> outputCols = getOutputCols(node, metadata);
ColStats newColStats = new ColStats();
for (int i = 0; i < outputCols.size(); i++) {
Expression expr = outputCols.get(i);
float[] newStats = new float[3];
Arrays.fill(newStats, UNKNOWN_VALUE);
if (childCardinality == UNKNOWN_VALUE || (setOp != null && (colStats == null || colStatsOther == null))) {
// base case - cannot determine, just assume unique rows
newStats[Stat.NDV.ordinal()] = cardinality;
newStats[Stat.NDV_HIGH.ordinal()] = cardinality;
newStats[Stat.NNV.ordinal()] = 0;
} else if (setOp != null) {
// set op
float[] stats = colStats.get(expr);
float[] statsOther = colStatsOther.get(outputColsOther.get(i));
newStats[Stat.NDV.ordinal()] = Math.min(cardinality, getCombinedSetEstimate(setOp, stats[Stat.NDV.ordinal()], statsOther[Stat.NDV.ordinal()], true));
newStats[Stat.NDV_HIGH.ordinal()] = Math.min(cardinality, getCombinedSetEstimate(setOp, stats[Stat.NDV_HIGH.ordinal()], statsOther[Stat.NDV_HIGH.ordinal()], true));
newStats[Stat.NNV.ordinal()] = Math.min(cardinality, getCombinedSetEstimate(setOp, stats[Stat.NNV.ordinal()], statsOther[Stat.NNV.ordinal()], !node.hasBooleanProperty(NodeConstants.Info.USE_ALL)));
} else {
// all other cases - join is the only multi-node case here
float[] stats = null;
float origCardinality = childCardinality;
boolean left = true;
if (colStats != null) {
stats = colStats.get(expr);
}
if (stats == null && colStatsOther != null) {
origCardinality = otherChildCardinality;
stats = colStatsOther.get(expr);
left = false;
}
origCardinality = Math.max(1, origCardinality);
if (stats == null) {
if (node.getType() == NodeConstants.Types.PROJECT) {
Collection<Expression> elems = new HashSet<Expression>();
ElementCollectorVisitor.getElements(expr, elems);
newStats[Stat.NDV.ordinal()] = getStat(Stat.NDV, elems, node, childCardinality, metadata);
newStats[Stat.NDV_HIGH.ordinal()] = getStat(Stat.NDV_HIGH, elems, node, childCardinality, metadata);
newStats[Stat.NNV.ordinal()] = getStat(Stat.NNV, elems, node, childCardinality, metadata);
} else {
// TODO: use a better estimate for new aggs
if (node.hasProperty(Info.GROUP_COLS)) {
newStats[Stat.NDV.ordinal()] = cardinality / 3;
newStats[Stat.NDV_HIGH.ordinal()] = cardinality / 3;
} else {
newStats[Stat.NDV.ordinal()] = cardinality;
newStats[Stat.NDV_HIGH.ordinal()] = cardinality;
}
newStats[Stat.NNV.ordinal()] = UNKNOWN_VALUE;
}
} else {
if (node.getType() == NodeConstants.Types.DUP_REMOVE || node.getType() == NodeConstants.Types.GROUP || node.getType() == NodeConstants.Types.PROJECT || node.getType() == NodeConstants.Types.ACCESS) {
// don't scale down
newStats[Stat.NDV.ordinal()] = Math.min(cardinality, stats[Stat.NDV.ordinal()]);
newStats[Stat.NDV_HIGH.ordinal()] = Math.min(cardinality, stats[Stat.NDV_HIGH.ordinal()]);
} else if (stats[Stat.NDV.ordinal()] != UNKNOWN_VALUE) {
if (stats[Stat.NDV.ordinal()] == stats[Stat.NDV_HIGH.ordinal()]) {
newStats[Stat.NDV.ordinal()] = stats[Stat.NDV.ordinal()] * Math.min(left ? leftPercent : rightPercent, cardinality / origCardinality);
newStats[Stat.NDV_HIGH.ordinal()] = newStats[Stat.NDV.ordinal()];
} else {
newStats[Stat.NDV.ordinal()] = (float) Math.min(stats[Stat.NDV.ordinal()], Math.sqrt(cardinality));
newStats[Stat.NDV_HIGH.ordinal()] = Math.min(stats[Stat.NDV_HIGH.ordinal()], cardinality / 2);
}
newStats[Stat.NDV.ordinal()] = Math.max(1, newStats[Stat.NDV.ordinal()]);
newStats[Stat.NDV_HIGH.ordinal()] = Math.max(1, newStats[Stat.NDV_HIGH.ordinal()]);
}
if (stats[Stat.NNV.ordinal()] != UNKNOWN_VALUE) {
// TODO: this is an under estimate for the inner side of outer joins
newStats[Stat.NNV.ordinal()] = stats[Stat.NNV.ordinal()] * Math.min(1, cardinality / origCardinality);
newStats[Stat.NNV.ordinal()] = Math.max(0, newStats[Stat.NNV.ordinal()]);
}
}
}
newColStats.put(expr, newStats);
}
node.setProperty(Info.EST_COL_STATS, newColStats);
}
use of org.teiid.query.sql.lang.SetQuery.Operation in project teiid by teiid.
the class RuleRaiseAccess method canRaiseOverSetQuery.
private static boolean canRaiseOverSetQuery(PlanNode setOpNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException {
Object modelID = null;
String sourceName = null;
boolean multiSource = false;
for (PlanNode childNode : setOpNode.getChildren()) {
if (childNode.getType() != NodeConstants.Types.ACCESS) {
return false;
}
if (FrameUtil.getNonQueryCommand(childNode) != null || FrameUtil.getNestedPlan(childNode) != null) {
return false;
}
// Get model and check that it exists
Object accessModelID = getModelIDFromAccess(childNode, metadata);
if (accessModelID == null) {
return false;
}
// TODO: see if the children are actually multiSourced
multiSource |= childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
String name = (String) childNode.getProperty(Info.SOURCE_NAME);
// Reconcile this access node's model ID with existing
if (modelID == null) {
modelID = accessModelID;
Operation op = (Operation) setOpNode.getProperty(NodeConstants.Info.SET_OPERATION);
if (!CapabilitiesUtil.supportsSetOp(accessModelID, op, metadata, capFinder)) {
return false;
}
if (multiSource && op != Operation.UNION) {
return false;
}
} else if (!CapabilitiesUtil.isSameConnector(modelID, accessModelID, metadata, capFinder)) {
return false;
}
if (!multiSource) {
if (sourceName == null) {
sourceName = name;
} else if (name != null && !sourceName.equals(name)) {
return false;
}
}
if (!setOpNode.hasBooleanProperty(NodeConstants.Info.USE_ALL) && !supportsDistinct(metadata, childNode, multiSource)) {
return false;
}
}
return true;
}
use of org.teiid.query.sql.lang.SetQuery.Operation in project teiid by teiid.
the class RulePlanUnions method optimizeUnions.
/**
* @param plan
* @param metadata
* @param capabilitiesFinder
* @throws QueryMetadataException
* @throws TeiidComponentException
*/
private void optimizeUnions(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder) throws QueryMetadataException, TeiidComponentException {
// look for all union branches and their sources
for (PlanNode unionNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SET_OP, NodeConstants.Types.SET_OP | NodeConstants.Types.ACCESS)) {
List<PlanNode> accessNodes = NodeEditor.findAllNodes(unionNode, NodeConstants.Types.ACCESS);
Object id = getModelId(metadata, accessNodes, capabilitiesFinder);
// check to see if this union is already to the same source
if (id != null) {
continue;
}
// a linked hashmap is used so that the first entry is logically the first branch
Map<Object, List<PlanNode>> sourceNodes = new LinkedHashMap<Object, List<PlanNode>>();
boolean all = unionNode.hasBooleanProperty(NodeConstants.Info.USE_ALL);
Operation op = (Operation) unionNode.getProperty(NodeConstants.Info.SET_OPERATION);
collectUnionSources(metadata, capabilitiesFinder, unionNode, sourceNodes, all, op);
if (sourceNodes.size() == 1) {
continue;
}
// rebuild unions based upon the source map
boolean shouldRebuild = false;
for (Map.Entry<Object, List<PlanNode>> entry : sourceNodes.entrySet()) {
if (entry.getKey() != null && entry.getValue().size() > 1 && CapabilitiesUtil.supportsSetOp(entry.getKey(), (Operation) unionNode.getProperty(NodeConstants.Info.SET_OPERATION), metadata, capabilitiesFinder)) {
shouldRebuild = true;
break;
}
}
if (!shouldRebuild) {
continue;
}
List<PlanNode> sourceUnions = new LinkedList<PlanNode>();
for (Map.Entry<Object, List<PlanNode>> entry : sourceNodes.entrySet()) {
List<PlanNode> sources = entry.getValue();
sourceUnions.add(buildUnionTree(unionNode, sources));
}
PlanNode tempRoot = buildUnionTree(unionNode, sourceUnions);
unionNode.removeAllChildren();
unionNode.addChildren(tempRoot.removeAllChildren());
}
}
use of org.teiid.query.sql.lang.SetQuery.Operation in project teiid by teiid.
the class PlanToProcessConverter method convertNode.
protected RelationalNode convertNode(PlanNode node) throws TeiidComponentException, TeiidProcessingException {
RelationalNode processNode = null;
switch(node.getType()) {
case NodeConstants.Types.PROJECT:
GroupSymbol intoGroup = (GroupSymbol) node.getProperty(NodeConstants.Info.INTO_GROUP);
if (intoGroup != null) {
try {
Insert insert = (Insert) node.getFirstChild().getProperty(Info.VIRTUAL_COMMAND);
List<ElementSymbol> allIntoElements = insert.getVariables();
Object groupID = intoGroup.getMetadataID();
Object modelID = metadata.getModelID(groupID);
String modelName = metadata.getFullName(modelID);
if (metadata.isVirtualGroup(groupID) && !metadata.isTemporaryTable(groupID)) {
InsertPlanExecutionNode ipen = new InsertPlanExecutionNode(getID(), metadata);
ProcessorPlan plan = (ProcessorPlan) node.getFirstChild().getProperty(Info.PROCESSOR_PLAN);
Assertion.isNotNull(plan);
ipen.setProcessorPlan(plan);
ipen.setReferences(insert.getValues());
processNode = ipen;
} else {
ProjectIntoNode pinode = new ProjectIntoNode(getID());
pinode.setIntoGroup(intoGroup);
pinode.setIntoElements(allIntoElements);
pinode.setModelName(modelName);
pinode.setConstraint((Criteria) node.getProperty(Info.CONSTRAINT));
pinode.setSourceHint((SourceHint) node.getProperty(Info.SOURCE_HINT));
if (node.hasBooleanProperty(Info.UPSERT)) {
pinode.setUpsert(true);
}
processNode = pinode;
SourceCapabilities caps = capFinder.findCapabilities(modelName);
if (caps.supportsCapability(Capability.INSERT_WITH_ITERATOR)) {
pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.ITERATOR);
} else if (caps.supportsCapability(Capability.BATCHED_UPDATES)) {
pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.BATCH);
} else {
pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.SINGLE);
}
pinode.setTransactionSupport((TransactionSupport) caps.getSourceProperty(Capability.TRANSACTION_SUPPORT));
}
} catch (QueryMetadataException e) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30247, e);
}
} else {
List<Expression> symbols = (List) node.getProperty(NodeConstants.Info.PROJECT_COLS);
ProjectNode pnode = new ProjectNode(getID());
pnode.setSelectSymbols(symbols);
processNode = pnode;
if (node.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
WindowFunctionProjectNode wfpn = new WindowFunctionProjectNode(getID());
// with partial projection the window function may already be pushed, we'll check for that here
ArrayList<Expression> filtered = new ArrayList<Expression>();
List<Expression> childSymbols = (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
for (Expression ex : symbols) {
ex = SymbolMap.getExpression(ex);
if (childSymbols.contains(ex)) {
continue;
}
filtered.add(ex);
}
Set<WindowFunction> windowFunctions = RuleAssignOutputElements.getWindowFunctions(filtered);
if (!windowFunctions.isEmpty()) {
// TODO: check for selecting all window functions
List<Expression> outputElements = new ArrayList<Expression>(windowFunctions);
// collect the other projected expressions
for (Expression singleElementSymbol : (List<Expression>) node.getFirstChild().getProperty(Info.OUTPUT_COLS)) {
outputElements.add(singleElementSymbol);
}
wfpn.setElements(outputElements);
wfpn.init();
pnode.addChild(wfpn);
for (WindowFunction wf : windowFunctions) {
validateAggregateFunctionEvaluation(wf.getFunction());
}
}
}
}
break;
case NodeConstants.Types.JOIN:
JoinType jtype = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
JoinStrategyType stype = (JoinStrategyType) node.getProperty(NodeConstants.Info.JOIN_STRATEGY);
JoinNode jnode = new JoinNode(getID());
jnode.setJoinType(jtype);
jnode.setLeftDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_LEFT_DISTINCT));
jnode.setRightDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_RIGHT_DISTINCT));
List joinCrits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
String depValueSource = (String) node.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE);
SortOption leftSort = (SortOption) node.getProperty(NodeConstants.Info.SORT_LEFT);
if (stype == JoinStrategyType.MERGE || stype == JoinStrategyType.ENHANCED_SORT) {
MergeJoinStrategy mjStrategy = null;
if (stype.equals(JoinStrategyType.ENHANCED_SORT)) {
EnhancedSortMergeJoinStrategy esmjStrategy = new EnhancedSortMergeJoinStrategy(leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT));
esmjStrategy.setSemiDep(node.hasBooleanProperty(Info.IS_SEMI_DEP));
mjStrategy = esmjStrategy;
} else {
mjStrategy = new MergeJoinStrategy(leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT), false);
}
jnode.setJoinStrategy(mjStrategy);
List leftExpressions = (List) node.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
List rightExpressions = (List) node.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
jnode.setJoinExpressions(leftExpressions, rightExpressions);
joinCrits = (List) node.getProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA);
} else if (stype == JoinStrategyType.NESTED_TABLE) {
NestedTableJoinStrategy ntjStrategy = new NestedTableJoinStrategy();
jnode.setJoinStrategy(ntjStrategy);
SymbolMap references = (SymbolMap) node.getProperty(Info.RIGHT_NESTED_REFERENCES);
ntjStrategy.setRightMap(references);
} else {
NestedLoopJoinStrategy nljStrategy = new NestedLoopJoinStrategy();
jnode.setJoinStrategy(nljStrategy);
}
Criteria joinCrit = Criteria.combineCriteria(joinCrits);
jnode.setJoinCriteria(joinCrit);
processNode = jnode;
jnode.setDependentValueSource(depValueSource);
break;
case NodeConstants.Types.ACCESS:
ProcessorPlan plan = (ProcessorPlan) node.getProperty(NodeConstants.Info.PROCESSOR_PLAN);
if (plan != null) {
PlanExecutionNode peNode = null;
Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA);
if (crit != null) {
List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS);
List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS);
peNode = new DependentProcedureExecutionNode(getID(), crit, references, defaults);
} else {
peNode = new PlanExecutionNode(getID());
}
peNode.setProcessorPlan(plan);
processNode = peNode;
} else {
AccessNode aNode = null;
Command command = (Command) node.getProperty(NodeConstants.Info.ATOMIC_REQUEST);
Object modelID = node.getProperty(NodeConstants.Info.MODEL_ID);
if (modelID != null) {
String fullName = metadata.getFullName(modelID);
if (!capFinder.isValid(fullName)) {
// TODO: we ideally want to handle the partial resutls case here differently
// by adding a null node / and a source warning
// for now it's just as easy to say that the user needs to take steps to
// return static capabilities
SourceCapabilities caps = capFinder.findCapabilities(fullName);
Exception cause = null;
if (caps != null) {
cause = (Exception) caps.getSourceProperty(Capability.INVALID_EXCEPTION);
}
throw new QueryPlannerException(QueryPlugin.Event.TEIID30498, cause, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30498, fullName));
}
}
EvaluatableVisitor ev = null;
if (node.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
if (command instanceof StoredProcedure) {
List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS);
List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS);
Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA);
DependentProcedureAccessNode depAccessNode = new DependentProcedureAccessNode(getID(), crit, references, defaults);
processNode = depAccessNode;
aNode = depAccessNode;
} else {
// create dependent access node
DependentAccessNode depAccessNode = new DependentAccessNode(getID());
if (modelID != null) {
depAccessNode.setPushdown(CapabilitiesUtil.supports(Capability.DEPENDENT_JOIN, modelID, metadata, capFinder));
depAccessNode.setMaxSetSize(CapabilitiesUtil.getMaxInCriteriaSize(modelID, metadata, capFinder));
depAccessNode.setMaxPredicates(CapabilitiesUtil.getMaxDependentPredicates(modelID, metadata, capFinder));
depAccessNode.setUseBindings(CapabilitiesUtil.supports(Capability.DEPENDENT_JOIN_BINDINGS, modelID, metadata, capFinder));
// TODO: allow the translator to drive this property
// simplistic check of whether this query is complex to re-execute
Query query = (Query) command;
if (query.getGroupBy() != null || query.getFrom().getClauses().size() > 1 || !(query.getFrom().getClauses().get(0) instanceof UnaryFromClause) || query.getWith() != null) {
depAccessNode.setComplexQuery(true);
} else {
// check to see if there in an index on at least one of the dependent sets
Set<GroupSymbol> groups = new HashSet<GroupSymbol>(query.getFrom().getGroups());
boolean found = false;
for (Criteria crit : Criteria.separateCriteriaByAnd(query.getCriteria())) {
if (crit instanceof DependentSetCriteria) {
DependentSetCriteria dsc = (DependentSetCriteria) crit;
if (NewCalculateCostUtil.getKeyUsed(ElementCollectorVisitor.getElements(dsc.getExpression(), true), groups, metadata, null) != null) {
found = true;
break;
}
}
}
if (!found) {
depAccessNode.setComplexQuery(true);
}
}
}
processNode = depAccessNode;
aNode = depAccessNode;
}
aNode.setShouldEvaluateExpressions(true);
} else {
// create access node
aNode = new AccessNode(getID());
processNode = aNode;
}
// -- special handling for system tables. currently they cannot perform projection
try {
if (command instanceof Query) {
processNode = correctProjectionInternalTables(node, aNode);
}
} catch (QueryMetadataException err) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30248, err);
}
setRoutingName(aNode, node, command);
boolean shouldEval = false;
if (command instanceof Insert) {
Insert insert = (Insert) command;
if (insert.getQueryExpression() != null) {
insert.setQueryExpression((QueryCommand) aliasCommand(aNode, insert.getQueryExpression(), modelID));
} else {
for (int i = 0; i < insert.getValues().size(); i++) {
Expression ex = (Expression) insert.getValues().get(i);
if (!CriteriaCapabilityValidatorVisitor.canPushLanguageObject(ex, modelID, metadata, capFinder, analysisRecord)) {
// replace with an expression symbol to let the rewriter know that it should be replaced
insert.getValues().set(i, new ExpressionSymbol("x", ex));
shouldEval = true;
}
}
}
} else if (command instanceof QueryCommand) {
command = aliasCommand(aNode, command, modelID);
}
ev = EvaluatableVisitor.needsEvaluationVisitor(modelID, metadata, capFinder);
if (!shouldEval && modelID != null) {
// do a capabilities sensitive check for needs eval
String modelName = metadata.getFullName(modelID);
SourceCapabilities caps = capFinder.findCapabilities(modelName);
final CriteriaCapabilityValidatorVisitor capVisitor = new CriteriaCapabilityValidatorVisitor(modelID, metadata, capFinder, caps);
capVisitor.setCheckEvaluation(false);
DeepPreOrderNavigator nav = new DeepPreOrderNavigator(ev) {
protected void visitNode(org.teiid.query.sql.LanguageObject obj) {
if (capVisitor.isValid() && obj instanceof Expression) {
obj.acceptVisitor(capVisitor);
}
super.visitNode(obj);
}
};
command.acceptVisitor(nav);
if (!capVisitor.isValid()) {
// there's a non-supported construct pushed, we should eval
ev.evaluationNotPossible(EvaluationLevel.PROCESSING);
}
} else {
DeepPreOrderNavigator.doVisit(command, ev);
}
aNode.setShouldEvaluateExpressions(ev.requiresEvaluation(EvaluationLevel.PROCESSING) || shouldEval);
aNode.setCommand(command);
if (modelID != null) {
String fullName = metadata.getFullName(modelID);
SourceCapabilities caps = capFinder.findCapabilities(fullName);
aNode.setTransactionSupport((TransactionSupport) caps.getSourceProperty(Capability.TRANSACTION_SUPPORT));
}
Map<GroupSymbol, PlanNode> subPlans = (Map<GroupSymbol, PlanNode>) node.getProperty(Info.SUB_PLANS);
// it makes more sense to allow the multisource affect to be elevated above just access nodes
if (aNode.getModelId() != null && metadata.isMultiSource(aNode.getModelId())) {
VDBMetaData vdb = context.getVdb();
// forces a rewrite
aNode.setShouldEvaluateExpressions(true);
aNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS));
if (node.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
Expression ex = rewriteMultiSourceCommand(aNode.getCommand());
aNode.setConnectorBindingExpression(ex);
aNode.setMultiSource(true);
} else {
String sourceName = (String) node.getProperty(Info.SOURCE_NAME);
aNode.setConnectorBindingExpression(new Constant(sourceName));
}
} else if (subPlans == null) {
if (!aNode.isShouldEvaluate()) {
aNode.minimizeProject(command);
}
// check if valid to share this with other nodes
if (ev != null && ev.getDeterminismLevel().compareTo(Determinism.INSTRUCTION_DETERMINISTIC) >= 0 && command.areResultsCachable()) {
checkForSharedSourceCommand(aNode, node);
}
}
if (subPlans != null) {
QueryCommand qc = (QueryCommand) command;
if (qc.getWith() == null) {
qc.setWith(new ArrayList<WithQueryCommand>(subPlans.size()));
}
Map<GroupSymbol, RelationalPlan> plans = new LinkedHashMap<GroupSymbol, RelationalPlan>();
for (Map.Entry<GroupSymbol, PlanNode> entry : subPlans.entrySet()) {
RelationalPlan subPlan = convert(entry.getValue());
List<ElementSymbol> elems = ResolverUtil.resolveElementsInGroup(entry.getKey(), metadata);
subPlan.setOutputElements(elems);
plans.put(entry.getKey(), subPlan);
WithQueryCommand withQueryCommand = new WithQueryCommand(entry.getKey(), elems, null);
qc.getWith().add(withQueryCommand);
}
aNode.setSubPlans(plans);
}
}
break;
case NodeConstants.Types.SELECT:
Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (!node.hasCollectionProperty(Info.OUTPUT_COLS)) {
// the late optimization to create a dependent join from a subquery introduces
// criteria that have no output elements set
// TODO that should be cleaner, but the logic currently expects to be run after join implementation
// and rerunning assign output elements seems excessive
node.setProperty(Info.OUTPUT_COLS, node.getFirstChild().getProperty(Info.OUTPUT_COLS));
}
SelectNode selnode = new SelectNode(getID());
selnode.setCriteria(crit);
// in case the parent was a source
selnode.setProjectedExpressions((List<Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS));
processNode = selnode;
break;
case NodeConstants.Types.SORT:
case NodeConstants.Types.DUP_REMOVE:
if (node.getType() == NodeConstants.Types.DUP_REMOVE) {
processNode = new DupRemoveNode(getID());
} else {
SortNode sortNode = new SortNode(getID());
OrderBy orderBy = (OrderBy) node.getProperty(NodeConstants.Info.SORT_ORDER);
if (orderBy != null) {
sortNode.setSortElements(orderBy.getOrderByItems());
}
if (node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)) {
sortNode.setMode(Mode.DUP_REMOVE_SORT);
}
processNode = sortNode;
}
break;
case NodeConstants.Types.GROUP:
GroupingNode gnode = new GroupingNode(getID());
gnode.setRollup(node.hasBooleanProperty(Info.ROLLUP));
SymbolMap groupingMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
gnode.setOutputMapping(groupingMap);
gnode.setRemoveDuplicates(node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL));
List<Expression> gCols = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
OrderBy orderBy = (OrderBy) node.getProperty(Info.SORT_ORDER);
if (orderBy == null) {
if (gCols != null) {
LinkedHashSet<Expression> exprs = new LinkedHashSet<Expression>();
for (Expression ex : gCols) {
exprs.add(SymbolMap.getExpression(ex));
}
orderBy = new OrderBy(RuleChooseJoinStrategy.createExpressionSymbols(new ArrayList<Expression>(exprs)));
}
} else {
HashSet<Expression> seen = new HashSet<Expression>();
for (int i = 0; i < gCols.size(); i++) {
if (i < orderBy.getOrderByItems().size()) {
OrderByItem orderByItem = orderBy.getOrderByItems().get(i);
Expression ex = SymbolMap.getExpression(orderByItem.getSymbol());
if (!seen.add(ex)) {
continue;
}
if (ex instanceof ElementSymbol) {
ex = groupingMap.getMappedExpression((ElementSymbol) ex);
// $NON-NLS-1$
orderByItem.setSymbol(new ExpressionSymbol("expr", ex));
}
} else {
// $NON-NLS-1$
orderBy.addVariable(new ExpressionSymbol("expr", gCols.get(i)), OrderBy.ASC);
}
}
}
if (orderBy != null) {
gnode.setOrderBy(orderBy.getOrderByItems());
}
for (Expression ex : groupingMap != null ? groupingMap.getValues() : (List<Expression>) node.getFirstChild().getProperty(NodeConstants.Info.PROJECT_COLS)) {
if (ex instanceof AggregateSymbol) {
validateAggregateFunctionEvaluation((AggregateSymbol) ex);
}
}
processNode = gnode;
break;
case NodeConstants.Types.SOURCE:
Object source = node.getProperty(NodeConstants.Info.TABLE_FUNCTION);
if (source instanceof XMLTable) {
XMLTable xt = (XMLTable) source;
XMLTableNode xtn = new XMLTableNode(getID());
// we handle the projection filtering once here rather than repeating the
// path analysis on a per plan basis
updateGroupName(node, xt);
Map<Expression, Integer> elementMap = RelationalNode.createLookupMap(xt.getProjectedSymbols());
List cols = (List) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols);
ArrayList<XMLColumn> filteredColumns = new ArrayList<XMLColumn>(projectionIndexes.length);
for (int col : projectionIndexes) {
filteredColumns.add(xt.getColumns().get(col));
}
xt.getXQueryExpression().useDocumentProjection(filteredColumns, analysisRecord);
xtn.setProjectedColumns(filteredColumns);
xtn.setTable(xt);
processNode = xtn;
break;
}
if (source instanceof ObjectTable) {
ObjectTable ot = (ObjectTable) source;
ObjectTableNode otn = new ObjectTableNode(getID());
// we handle the projection filtering once here rather than repeating the
// path analysis on a per plan basis
updateGroupName(node, ot);
Map<Expression, Integer> elementMap = RelationalNode.createLookupMap(ot.getProjectedSymbols());
List<Expression> cols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols);
ArrayList<ObjectColumn> filteredColumns = new ArrayList<ObjectColumn>(projectionIndexes.length);
for (int col : projectionIndexes) {
filteredColumns.add(ot.getColumns().get(col));
}
otn.setProjectedColumns(filteredColumns);
otn.setTable(ot);
processNode = otn;
break;
}
if (source instanceof TextTable) {
TextTableNode ttn = new TextTableNode(getID());
TextTable tt = (TextTable) source;
updateGroupName(node, tt);
ttn.setTable(tt);
processNode = ttn;
break;
}
if (source instanceof ArrayTable) {
ArrayTableNode atn = new ArrayTableNode(getID());
ArrayTable at = (ArrayTable) source;
updateGroupName(node, at);
atn.setTable(at);
processNode = atn;
break;
}
SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
if (symbolMap != null) {
PlanNode child = node.getLastChild();
if (child.getType() == NodeConstants.Types.PROJECT || child.getType() == NodeConstants.Types.SELECT) {
// update the project cols based upon the original output
child.setProperty(NodeConstants.Info.PROJECT_COLS, child.getProperty(NodeConstants.Info.OUTPUT_COLS));
}
if (child.getType() != NodeConstants.Types.SET_OP || child.getProperty(Info.SET_OPERATION) == Operation.UNION) {
child.setProperty(NodeConstants.Info.OUTPUT_COLS, node.getProperty(NodeConstants.Info.OUTPUT_COLS));
} else {
// else we cannot directly update the child properties as the child will get converted to a join
// create a projection instead for initialization purposes, but won't impact performance
ProjectNode pNode = new ProjectNode(getID());
pNode.setSelectSymbols((List<? extends Expression>) child.getProperty(NodeConstants.Info.OUTPUT_COLS));
return prepareToAdd(node, pNode);
}
}
return null;
case NodeConstants.Types.SET_OP:
Operation setOp = (Operation) node.getProperty(NodeConstants.Info.SET_OPERATION);
boolean useAll = ((Boolean) node.getProperty(NodeConstants.Info.USE_ALL)).booleanValue();
if (setOp == Operation.UNION) {
RelationalNode unionAllNode = new UnionAllNode(getID());
if (useAll) {
processNode = unionAllNode;
} else {
boolean onlyDupRemoval = node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL);
if (onlyDupRemoval) {
processNode = new DupRemoveNode(getID());
} else {
SortNode sNode = new SortNode(getID());
sNode.setMode(Mode.DUP_REMOVE_SORT);
processNode = sNode;
}
unionAllNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS));
processNode.addChild(unionAllNode);
}
} else {
JoinNode joinAsSet = new JoinNode(getID());
joinAsSet.setJoinStrategy(new MergeJoinStrategy(SortOption.SORT_DISTINCT, SortOption.SORT_DISTINCT, true));
// If we push these sorts, we will have to enforce null order, since nulls are equal here
List leftExpressions = (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
List rightExpressions = (List) node.getLastChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
joinAsSet.setJoinType(setOp == Operation.EXCEPT ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI);
joinAsSet.setJoinExpressions(leftExpressions, rightExpressions);
processNode = joinAsSet;
}
break;
case NodeConstants.Types.TUPLE_LIMIT:
Expression rowLimit = (Expression) node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
Expression offset = (Expression) node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
LimitNode ln = new LimitNode(getID(), rowLimit, offset);
ln.setImplicit(node.hasBooleanProperty(Info.IS_IMPLICIT_LIMIT));
processNode = ln;
break;
case NodeConstants.Types.NULL:
processNode = new NullNode(getID());
break;
default:
throw new QueryPlannerException(QueryPlugin.Event.TEIID30250, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30250, NodeConstants.getNodeTypeString(node.getType())));
}
if (processNode != null) {
processNode = prepareToAdd(node, processNode);
}
return processNode;
}
use of org.teiid.query.sql.lang.SetQuery.Operation in project teiid by teiid.
the class RuleCollapseSource method createQuery.
private QueryCommand createQuery(CommandContext context, CapabilitiesFinder capFinder, PlanNode accessRoot, PlanNode node) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
QueryMetadataInterface metadata = context.getMetadata();
PlanNode setOpNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE);
Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessRoot, metadata);
if (setOpNode != null) {
Operation setOp = (Operation) setOpNode.getProperty(NodeConstants.Info.SET_OPERATION);
SetQuery unionCommand = new SetQuery(setOp);
boolean unionAll = ((Boolean) setOpNode.getProperty(NodeConstants.Info.USE_ALL)).booleanValue();
unionCommand.setAll(unionAll);
int count = 0;
OrderBy orderBy = null;
PlanNode sort = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SORT, NodeConstants.Types.SET_OP);
if (sort != null) {
processOrderBy(sort, unionCommand, modelID, context, capFinder);
orderBy = unionCommand.getOrderBy();
unionCommand.setOrderBy(null);
// we have to remap if the primary projection is from a grouping
PlanNode groupNode = NodeEditor.findNodePreOrder(setOpNode.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE);
if (groupNode != null) {
SymbolMap symbolMap = (SymbolMap) groupNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
ExpressionMappingVisitor.mapExpressions(orderBy, symbolMap.asMap(), true);
}
}
for (PlanNode child : setOpNode.getChildren()) {
QueryCommand command = createQuery(context, capFinder, accessRoot, child);
if (count == 0) {
unionCommand.setLeftQuery(command);
} else if (count == 1) {
unionCommand.setRightQuery(command);
} else {
unionCommand = new SetQuery(setOp, unionAll, unionCommand, command);
}
count++;
}
PlanNode limit = NodeEditor.findNodePreOrder(node, NodeConstants.Types.TUPLE_LIMIT, NodeConstants.Types.SET_OP);
if (limit != null) {
processLimit(limit, unionCommand, metadata);
}
unionCommand.setOrderBy(orderBy);
return unionCommand;
}
Query query = new Query();
Select select = new Select();
List<Expression> columns = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
prepareSubqueries(ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(columns));
select.addSymbols(columns);
query.setSelect(select);
query.setFrom(new From());
buildQuery(accessRoot, node, query, context, capFinder);
if (!CapabilitiesUtil.useAnsiJoin(modelID, metadata, capFinder)) {
simplifyFromClause(query);
}
if (query.getCriteria() instanceof CompoundCriteria) {
query.setCriteria(QueryRewriter.optimizeCriteria((CompoundCriteria) query.getCriteria(), metadata));
}
if (columns.isEmpty()) {
if (CapabilitiesUtil.supports(Capability.QUERY_SELECT_EXPRESSION, modelID, metadata, capFinder)) {
// $NON-NLS-1$
select.addSymbol(new ExpressionSymbol("dummy", new Constant(1)));
} else {
// TODO: need to ensure the type is consistent
// - should be rare as the source would typically support select expression if it supports union
select.addSymbol(selectOutputElement(query.getFrom().getGroups(), metadata));
}
}
PlanNode groupNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE);
if (groupNode != null) {
if (query.getOrderBy() != null) {
query.setOrderBy(query.getOrderBy().clone());
}
if (query.getHaving() != null) {
query.setHaving((Criteria) query.getHaving().clone());
}
query.setSelect(query.getSelect().clone());
SymbolMap symbolMap = (SymbolMap) groupNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
// map back to expression form
ExpressionMappingVisitor.mapExpressions(query.getOrderBy(), symbolMap.asMap(), true);
ExpressionMappingVisitor.mapExpressions(query.getSelect(), symbolMap.asMap(), true);
ExpressionMappingVisitor.mapExpressions(query.getHaving(), symbolMap.asMap(), true);
if (query.getHaving() != null && !CapabilitiesUtil.supports(Capability.QUERY_HAVING, modelID, metadata, capFinder)) {
Select sel = query.getSelect();
GroupBy groupBy = query.getGroupBy();
Criteria having = query.getHaving();
query.setHaving(null);
OrderBy orderBy = query.getOrderBy();
query.setOrderBy(null);
Limit limit = query.getLimit();
query.setLimit(null);
Set<AggregateSymbol> aggs = new HashSet<AggregateSymbol>();
aggs.addAll(AggregateSymbolCollectorVisitor.getAggregates(having, true));
Set<Expression> expr = new HashSet<Expression>();
for (Expression ex : sel.getProjectedSymbols()) {
Expression selectExpression = SymbolMap.getExpression(ex);
aggs.remove(selectExpression);
expr.add(selectExpression);
}
int originalSelect = sel.getSymbols().size();
sel.addSymbols(aggs);
if (groupBy != null) {
for (Expression ex : groupBy.getSymbols()) {
ex = SymbolMap.getExpression(ex);
if (expr.add(ex)) {
sel.addSymbol(ex);
}
}
}
Query outerQuery = null;
try {
// $NON-NLS-1$
outerQuery = QueryRewriter.createInlineViewQuery(new GroupSymbol("X"), query, metadata, query.getSelect().getProjectedSymbols());
} catch (TeiidException err) {
throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30257, err);
}
Iterator<Expression> iter = outerQuery.getSelect().getProjectedSymbols().iterator();
HashMap<Expression, Expression> expressionMap = new HashMap<Expression, Expression>();
for (Expression symbol : query.getSelect().getProjectedSymbols()) {
// need to unwrap on both sides as the select expression could be aliased
// TODO: could add an option to createInlineViewQuery to disable alias creation
expressionMap.put(SymbolMap.getExpression(symbol), SymbolMap.getExpression(iter.next()));
}
ExpressionMappingVisitor.mapExpressions(having, expressionMap, true);
outerQuery.setCriteria(having);
ExpressionMappingVisitor.mapExpressions(orderBy, expressionMap, true);
outerQuery.setOrderBy(orderBy);
outerQuery.setLimit(limit);
ExpressionMappingVisitor.mapExpressions(select, expressionMap, true);
outerQuery.getSelect().setSymbols(outerQuery.getSelect().getProjectedSymbols().subList(0, originalSelect));
outerQuery.setOption(query.getOption());
query = outerQuery;
}
if (query.getGroupBy() != null) {
// we check for group by expressions here to create an ANSI SQL plan
boolean hasExpression = false;
boolean hasLiteral = false;
for (final Iterator<Expression> iterator = query.getGroupBy().getSymbols().iterator(); iterator.hasNext(); ) {
Expression ex = iterator.next();
hasExpression |= !(ex instanceof ElementSymbol);
hasLiteral |= EvaluatableVisitor.willBecomeConstant(ex, true);
}
if ((hasExpression && !CapabilitiesUtil.supports(Capability.QUERY_FUNCTIONS_IN_GROUP_BY, modelID, metadata, capFinder)) || hasLiteral) {
// if group by expressions are not support, add an inline view to compensate
query = RuleCollapseSource.rewriteGroupByAsView(query, metadata, false);
if (query.getHaving() != null) {
// dependent sets will have been added a having
List<Criteria> crits = Criteria.separateCriteriaByAnd(query.getHaving());
for (Iterator<Criteria> iter = crits.iterator(); iter.hasNext(); ) {
Criteria crit = iter.next();
if (crit instanceof DependentSetCriteria) {
query.setCriteria(Criteria.combineCriteria(query.getCriteria(), crit));
iter.remove();
}
}
query.setHaving(Criteria.combineCriteria(crits));
}
}
if (query.getOrderBy() != null && groupNode.hasBooleanProperty(Info.ROLLUP) && !CapabilitiesUtil.supports(Capability.QUERY_ORDERBY_EXTENDED_GROUPING, modelID, metadata, capFinder)) {
// if ordering is not directly supported over extended grouping, add an inline view to compensate
query = RuleCollapseSource.rewriteGroupByAsView(query, metadata, true);
}
}
}
return query;
}
Aggregations