use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RuleAccessPatternValidation method validateAccessPatterns.
/**
* @param node
* @throws QueryPlannerException
*/
private void validateAccessPatterns(PlanNode node) throws QueryPlannerException {
if (!node.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
return;
}
Criteria criteria = null;
if (node.hasProperty(NodeConstants.Info.ATOMIC_REQUEST)) {
Object req = node.getProperty(NodeConstants.Info.ATOMIC_REQUEST);
if (req instanceof Insert) {
return;
}
if (req instanceof Delete) {
criteria = ((Delete) req).getCriteria();
} else if (req instanceof Update) {
criteria = ((Update) req).getCriteria();
}
}
List accessPatterns = (List) node.getProperty(NodeConstants.Info.ACCESS_PATTERNS);
if (criteria != null) {
for (Criteria crit : Criteria.separateCriteriaByAnd(criteria)) {
Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(crit, true);
if (RulePushSelectCriteria.satisfyAccessPatterns(accessPatterns, elements)) {
return;
}
}
}
Object groups = node.getGroups();
throw new QueryPlannerException(QueryPlugin.Event.TEIID30278, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30278, new Object[] { groups, accessPatterns }));
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RuleApplySecurity method execute.
@Override
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
try {
for (PlanNode sourceNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE)) {
GroupSymbol group = sourceNode.getGroups().iterator().next();
if (!RowBasedSecurityHelper.applyRowSecurity(metadata, group, context)) {
continue;
}
List<ElementSymbol> cols = null;
Command command = (Command) sourceNode.getProperty(Info.VIRTUAL_COMMAND);
if (group.isProcedure()) {
if (command == null) {
// proc relational, will instead apply at the proc level
continue;
}
if (cols == null) {
cols = (List) command.getProjectedSymbols();
}
} else if (command != null && !command.returnsResultSet()) {
// should be handled in the planner
continue;
}
if (cols == null) {
cols = ResolverUtil.resolveElementsInGroup(group, metadata);
}
// apply masks first
List<? extends Expression> masked = ColumnMaskingHelper.maskColumns(cols, group, metadata, context);
Map<ElementSymbol, Expression> mapping = null;
// TODO: we don't actually allow window function masks yet becuase they won't pass
// validation. but if we do, we need to check for them here
List<WindowFunction> windowFunctions = new ArrayList<WindowFunction>(2);
for (int i = 0; i < masked.size(); i++) {
Expression maskedCol = masked.get(i);
AggregateSymbolCollectorVisitor.getAggregates(maskedCol, null, null, null, windowFunctions, null);
if (maskedCol.equals(cols.get(i))) {
continue;
}
if (mapping == null) {
mapping = new HashMap<ElementSymbol, Expression>();
}
mapping.put(cols.get(i), maskedCol);
}
PlanNode parentJoin = NodeEditor.findParent(sourceNode.getParent(), NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
if (mapping != null) {
// some element symbol has been replaced
PlanNode project = null;
if (group.isProcedure()) {
project = NodeEditor.findParent(sourceNode, NodeConstants.Types.PROJECT);
project.setProperty(NodeConstants.Info.PROJECT_COLS, masked);
}
if (windowFunctions.isEmpty() && RuleMergeVirtual.checkProjectedSymbols(group, parentJoin, metadata, masked, Collections.singleton(group), true)) {
if (!group.isProcedure()) {
// just upwardly project - TODO: we could also handle some subquery simple projection situations here
FrameUtil.convertFrame(sourceNode.getParent(), group, Collections.singleton(group), mapping, metadata);
}
} else {
if (!group.isProcedure()) {
project = RelationalPlanner.createProjectNode(masked);
}
rules.getPlanner().planSubqueries(sourceNode.getGroups(), project, project.getSubqueryContainers(), true);
project.addGroups(GroupsUsedByElementsVisitor.getGroups(project.getCorrelatedReferenceElements()));
if (!group.isProcedure()) {
// we need to insert a view to give a single place to evaluate the subqueries
PlanNode root = sourceNode;
if (sourceNode.getParent().getType() == NodeConstants.Types.ACCESS) {
root = sourceNode.getParent();
}
root.addAsParent(project);
addView(metadata, context, group, cols, masked, project);
parentJoin = null;
}
}
if (!windowFunctions.isEmpty() && project != null) {
project.setProperty(Info.HAS_WINDOW_FUNCTIONS, true);
}
}
// logically filters are applied below masking
Criteria filter = RowBasedSecurityHelper.getRowBasedFilters(metadata, group, context, false);
if (filter == null) {
continue;
}
List<Criteria> crits = Criteria.separateCriteriaByAnd(filter);
PlanNode root = sourceNode;
if (sourceNode.getParent().getType() == NodeConstants.Types.ACCESS) {
root = sourceNode.getParent();
}
PlanNode parent = null;
for (Criteria crit : crits) {
PlanNode critNode = RelationalPlanner.createSelectNode(crit, false);
if (parent == null) {
parent = critNode;
}
rules.getPlanner().planSubqueries(sourceNode.getGroups(), critNode, critNode.getSubqueryContainers(), true);
critNode.addGroups(GroupsUsedByElementsVisitor.getGroups(critNode.getCorrelatedReferenceElements()));
root.addAsParent(critNode);
}
if (!RuleMergeVirtual.checkJoinCriteria(parent, group, parentJoin)) {
PlanNode project = RelationalPlanner.createProjectNode(cols);
parent.addAsParent(project);
// a view is needed to keep the logical placement of the criteria
addView(metadata, context, group, cols, cols, project);
}
}
} catch (TeiidProcessingException e) {
throw new QueryPlannerException(e);
}
return plan;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RuleAssignOutputElements method determineSourceOutput.
/**
* A special case to consider is when the virtual group is defined by a
* UNION (no ALL) or a SELECT DISTINCT. In this case, the dup removal means
* that all columns need to be used to determine duplicates. So, filtering the
* columns at all will alter the number of rows flowing through the frame.
* So, in this case filtering should not occur. In fact the output columns
* that were set on root above are filtered, but we actually want all the
* virtual elements - so just reset it and proceed as before
* @throws TeiidComponentException
* @throws QueryMetadataException
* @throws QueryPlannerException
*/
static List<? extends Expression> determineSourceOutput(PlanNode root, List<Expression> outputElements, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
PlanNode virtualRoot = root.getLastChild();
if (hasDupRemoval(virtualRoot)) {
// Reset the outputColumns for this source node to be all columns for the virtual group
SymbolMap symbolMap = (SymbolMap) root.getProperty(NodeConstants.Info.SYMBOL_MAP);
if (!symbolMap.asMap().keySet().containsAll(outputElements)) {
outputElements.removeAll(symbolMap.asMap().keySet());
throw new QueryPlannerException(QueryPlugin.Event.TEIID30259, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30259, outputElements));
}
return symbolMap.getKeys();
}
PlanNode limit = NodeEditor.findNodePreOrder(root, NodeConstants.Types.TUPLE_LIMIT, NodeConstants.Types.PROJECT);
if (limit == null) {
return outputElements;
}
// reset the output elements to be the output columns + what's required by the sort
PlanNode sort = NodeEditor.findNodePreOrder(limit, NodeConstants.Types.SORT, NodeConstants.Types.PROJECT);
if (sort == null) {
return outputElements;
}
PlanNode access = NodeEditor.findParent(sort, NodeConstants.Types.ACCESS);
if (sort.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT) || (access != null && capFinder != null && CapabilitiesUtil.supports(Capability.QUERY_ORDERBY_UNRELATED, RuleRaiseAccess.getModelIDFromAccess(access, metadata), metadata, capFinder))) {
return outputElements;
}
OrderBy sortOrder = (OrderBy) sort.getProperty(NodeConstants.Info.SORT_ORDER);
List<Expression> topCols = FrameUtil.findTopCols(sort);
SymbolMap symbolMap = (SymbolMap) root.getProperty(NodeConstants.Info.SYMBOL_MAP);
List<ElementSymbol> symbolOrder = symbolMap.getKeys();
for (OrderByItem item : sortOrder.getOrderByItems()) {
final Expression expr = item.getSymbol();
int index = topCols.indexOf(expr);
if (index < 0) {
continue;
}
ElementSymbol symbol = symbolOrder.get(index);
if (!outputElements.contains(symbol)) {
outputElements.add(symbol);
}
}
return outputElements;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class QueryOptimizer method planProcedure.
private static ProcessorPlan planProcedure(Command command, QueryMetadataInterface metadata, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws TeiidComponentException, QueryPlannerException, QueryMetadataException {
ProcessorPlan result;
try {
command = QueryRewriter.rewrite(command, metadata, context);
} catch (TeiidProcessingException e) {
throw new QueryPlannerException(e);
}
result = PROCEDURE_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context);
return result;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class QueryOptimizer method optimizePlan.
public static ProcessorPlan optimizePlan(Command command, QueryMetadataInterface metadata, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
if (analysisRecord == null) {
analysisRecord = new AnalysisRecord(false, false);
}
if (context == null) {
context = new CommandContext();
}
if (!(capFinder instanceof TempCapabilitiesFinder)) {
capFinder = new TempCapabilitiesFinder(capFinder);
}
boolean debug = analysisRecord.recordDebug();
if (!(metadata instanceof TempMetadataAdapter)) {
metadata = new TempMetadataAdapter(metadata, new TempMetadataStore());
}
if (context.getMetadata() == null) {
context.setMetadata(metadata);
}
// Create an ID generator that can be used for all plans to generate unique data node IDs
if (idGenerator == null) {
idGenerator = new IDGenerator();
}
if (debug) {
// $NON-NLS-1$
analysisRecord.println("\n----------------------------------------------------------------------------");
// $NON-NLS-1$
analysisRecord.println("OPTIMIZE: \n" + command);
}
if (command instanceof Insert) {
Insert insert = (Insert) command;
if (insert.isUpsert()) {
// if not supported or there are trigger actions, then rewrite as a procedure
// we do this here since we're changing the command type.
// TODO: we could push this back into the rewrite, but it will need to be capabilities aware
GroupSymbol group = insert.getGroup();
Object modelId = metadata.getModelID(group.getMetadataID());
boolean supportsUpsert = CapabilitiesUtil.supports(Capability.UPSERT, modelId, metadata, capFinder);
if (!supportsUpsert) {
try {
command = QueryRewriter.rewriteAsUpsertProcedure(insert, metadata, context);
} catch (TeiidProcessingException e) {
throw new QueryPlannerException(e);
}
if (debug) {
// $NON-NLS-1$
analysisRecord.println("\n----------------------------------------------------------------------------");
// $NON-NLS-1$
analysisRecord.println("OPTIMIZE UPSERT PROCEDURE: \n" + command);
}
}
}
}
ProcessorPlan result = null;
switch(command.getType()) {
case Command.TYPE_UPDATE_PROCEDURE:
CreateProcedureCommand cupc = (CreateProcedureCommand) command;
if (cupc.getUpdateType() != Command.TYPE_UNKNOWN || cupc.getVirtualGroup() == null) {
// row update procedure or anon block
result = planProcedure(command, metadata, idGenerator, capFinder, analysisRecord, context);
} else {
Object pid = cupc.getVirtualGroup().getMetadataID();
if (pid instanceof TempMetadataID) {
TempMetadataID tid = (TempMetadataID) pid;
if (tid.getOriginalMetadataID() != null) {
pid = tid.getOriginalMetadataID();
}
}
String fullName = metadata.getFullName(pid);
// $NON-NLS-1$
fullName = "procedure cache:" + fullName;
PreparedPlan pp = context.getPlan(fullName);
if (pp == null) {
Determinism determinismLevel = context.resetDeterminismLevel();
try {
CommandContext clone = context.clone();
ProcessorPlan plan = planProcedure(command, metadata, idGenerator, capFinder, analysisRecord, clone);
// note that this is not a full prepared plan. It is not usable by user queries.
if (pid instanceof Procedure) {
clone.accessedPlanningObject(pid);
}
pp = new PreparedPlan();
pp.setPlan(plan, clone);
context.putPlan(fullName, pp, context.getDeterminismLevel());
} finally {
context.setDeterminismLevel(determinismLevel);
}
}
result = pp.getPlan().clone();
for (Object id : pp.getAccessInfo().getObjectsAccessed()) {
context.accessedPlanningObject(id);
}
}
break;
case Command.TYPE_BATCHED_UPDATE:
result = BATCHED_UPDATE_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context);
break;
case Command.TYPE_ALTER_PROC:
case Command.TYPE_ALTER_TRIGGER:
case Command.TYPE_ALTER_VIEW:
result = DDL_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context);
break;
case Command.TYPE_SOURCE_EVENT:
result = SOURCE_EVENT_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context);
break;
default:
try {
RelationalPlanner planner = new RelationalPlanner();
planner.initialize(command, idGenerator, metadata, capFinder, analysisRecord, context);
result = planner.optimize(command);
} catch (QueryResolverException e) {
throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30245, e);
}
}
if (debug) {
// $NON-NLS-1$
analysisRecord.println("\n----------------------------------------------------------------------------");
// $NON-NLS-1$
analysisRecord.println("OPTIMIZATION COMPLETE:");
// $NON-NLS-1$
analysisRecord.println("PROCESSOR PLAN:\n" + result);
// $NON-NLS-1$
analysisRecord.println("============================================================================");
}
return result;
}
Aggregations