1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.query.plan;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import org.modeshape.graph.GraphI18n;
32 import org.modeshape.graph.query.QueryContext;
33 import org.modeshape.graph.query.model.AllNodes;
34 import org.modeshape.graph.query.model.And;
35 import org.modeshape.graph.query.model.Column;
36 import org.modeshape.graph.query.model.Constraint;
37 import org.modeshape.graph.query.model.FullTextSearch;
38 import org.modeshape.graph.query.model.Join;
39 import org.modeshape.graph.query.model.JoinType;
40 import org.modeshape.graph.query.model.Limit;
41 import org.modeshape.graph.query.model.NamedSelector;
42 import org.modeshape.graph.query.model.Ordering;
43 import org.modeshape.graph.query.model.Query;
44 import org.modeshape.graph.query.model.QueryCommand;
45 import org.modeshape.graph.query.model.Selector;
46 import org.modeshape.graph.query.model.SelectorName;
47 import org.modeshape.graph.query.model.SetQuery;
48 import org.modeshape.graph.query.model.Source;
49 import org.modeshape.graph.query.model.Visitors;
50 import org.modeshape.graph.query.plan.PlanNode.Property;
51 import org.modeshape.graph.query.plan.PlanNode.Type;
52 import org.modeshape.graph.query.validate.Schemata;
53 import org.modeshape.graph.query.validate.Validator;
54 import org.modeshape.graph.query.validate.Schemata.Table;
55 import org.modeshape.graph.query.validate.Schemata.View;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public class CanonicalPlanner implements Planner {
94
95
96
97
98
99
100
101 public PlanNode createPlan( QueryContext context,
102 QueryCommand query ) {
103 PlanNode plan = null;
104 if (query instanceof Query) {
105 plan = createCanonicalPlan(context, (Query)query);
106 } else {
107 plan = createCanonicalPlan(context, (SetQuery)query);
108 }
109 return plan;
110 }
111
112
113
114
115
116
117
118
119 protected PlanNode createCanonicalPlan( QueryContext context,
120 Query query ) {
121 PlanNode plan = null;
122
123
124 Map<SelectorName, Table> usedSources = new HashMap<SelectorName, Table>();
125 plan = createPlanNode(context, query.source(), usedSources);
126
127
128 plan = attachCriteria(context, plan, query.constraint());
129
130
131
132
133
134 plan = attachProject(context, plan, query.columns(), usedSources);
135
136
137 if (query.isDistinct()) {
138 plan = attachDuplicateRemoval(context, plan);
139 }
140
141
142 plan = attachSorting(context, plan, query.orderings());
143 plan = attachLimits(context, plan, query.limits());
144
145
146 validate(context, query, usedSources);
147
148 return plan;
149 }
150
151
152
153
154
155
156
157
158 protected void validate( QueryContext context,
159 QueryCommand query,
160 Map<SelectorName, Table> usedSelectors ) {
161
162 Visitors.visitAll(query, new Validator(context, usedSelectors));
163 }
164
165
166
167
168
169
170
171
172 protected PlanNode createCanonicalPlan( QueryContext context,
173 SetQuery query ) {
174
175 PlanNode left = createPlan(context, query.left());
176 PlanNode right = createPlan(context, query.right());
177
178
179 PlanNode plan = new PlanNode(Type.SET_OPERATION);
180 plan.addChildren(left, right);
181 plan.setProperty(Property.SET_OPERATION, query.operation());
182 plan.setProperty(Property.SET_USE_ALL, query.isAll());
183
184
185 plan = attachSorting(context, plan, query.orderings());
186 plan = attachLimits(context, plan, query.limits());
187 return plan;
188 }
189
190
191
192
193
194
195
196
197
198 protected PlanNode createPlanNode( QueryContext context,
199 Source source,
200 Map<SelectorName, Table> usedSelectors ) {
201 if (source instanceof Selector) {
202
203 assert source instanceof AllNodes || source instanceof NamedSelector;
204 Selector selector = (Selector)source;
205 PlanNode node = new PlanNode(Type.SOURCE);
206 if (selector.hasAlias()) {
207 node.addSelector(selector.alias());
208 node.setProperty(Property.SOURCE_ALIAS, selector.alias());
209 node.setProperty(Property.SOURCE_NAME, selector.name());
210 } else {
211 node.addSelector(selector.name());
212 node.setProperty(Property.SOURCE_NAME, selector.name());
213 }
214
215 Table table = context.getSchemata().getTable(selector.name());
216 if (table != null) {
217 if (table instanceof View) context.getHints().hasView = true;
218 if (usedSelectors.put(selector.aliasOrName(), table) != null) {
219
220 }
221 node.setProperty(Property.SOURCE_COLUMNS, table.getColumns());
222 } else {
223 context.getProblems().addError(GraphI18n.tableDoesNotExist, selector.name());
224 }
225 return node;
226 }
227 if (source instanceof Join) {
228 Join join = (Join)source;
229
230 PlanNode node = new PlanNode(Type.JOIN);
231 node.setProperty(Property.JOIN_TYPE, join.type());
232 node.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
233 node.setProperty(Property.JOIN_CONDITION, join.joinCondition());
234
235 context.getHints().hasJoin = true;
236 if (join.type() == JoinType.LEFT_OUTER) {
237 context.getHints().hasOptionalJoin = true;
238 }
239
240
241 Source[] clauses = new Source[] {join.left(), join.right()};
242 for (int i = 0; i < 2; i++) {
243 PlanNode sourceNode = createPlanNode(context, clauses[i], usedSelectors);
244 node.addLastChild(sourceNode);
245
246
247 for (PlanNode child : node.getChildren()) {
248 node.addSelectors(child.getSelectors());
249 }
250 }
251 return node;
252 }
253
254 assert false;
255 return null;
256 }
257
258
259
260
261
262
263
264
265
266 protected PlanNode attachCriteria( final QueryContext context,
267 PlanNode plan,
268 Constraint constraint ) {
269 if (constraint == null) return plan;
270 context.getHints().hasCriteria = true;
271
272
273 LinkedList<Constraint> andableConstraints = new LinkedList<Constraint>();
274 separateAndConstraints(constraint, andableConstraints);
275 assert !andableConstraints.isEmpty();
276
277
278
279 while (!andableConstraints.isEmpty()) {
280 Constraint criteria = andableConstraints.removeLast();
281
282 PlanNode criteriaNode = new PlanNode(Type.SELECT);
283 criteriaNode.setProperty(Property.SELECT_CRITERIA, criteria);
284
285
286 criteriaNode.addSelectors(Visitors.getSelectorsReferencedBy(criteria));
287
288
289 Visitors.visitAll(criteria, new Visitors.AbstractVisitor() {
290 @Override
291 public void visit( FullTextSearch obj ) {
292 context.getHints().hasFullTextSearch = true;
293 }
294 });
295
296 criteriaNode.addFirstChild(plan);
297 plan = criteriaNode;
298 }
299 return plan;
300 }
301
302
303
304
305
306
307
308
309
310 protected void separateAndConstraints( Constraint constraint,
311 List<Constraint> andableConstraints ) {
312 if (constraint == null) return;
313 assert andableConstraints != null;
314 if (constraint instanceof And) {
315 And and = (And)constraint;
316 separateAndConstraints(and.left(), andableConstraints);
317 separateAndConstraints(and.right(), andableConstraints);
318 } else {
319 andableConstraints.add(constraint);
320 }
321 }
322
323
324
325
326
327
328
329
330
331 protected PlanNode attachSorting( QueryContext context,
332 PlanNode plan,
333 List<? extends Ordering> orderings ) {
334 if (orderings.isEmpty()) return plan;
335 PlanNode sortNode = new PlanNode(Type.SORT);
336
337 context.getHints().hasSort = true;
338 sortNode.setProperty(Property.SORT_ORDER_BY, orderings);
339 for (Ordering ordering : orderings) {
340 sortNode.addSelectors(Visitors.getSelectorsReferencedBy(ordering));
341 }
342
343 sortNode.addLastChild(plan);
344 return sortNode;
345 }
346
347
348
349
350
351
352
353
354
355 protected PlanNode attachLimits( QueryContext context,
356 PlanNode plan,
357 Limit limit ) {
358 if (limit.isUnlimited()) return plan;
359 context.getHints().hasLimit = true;
360 PlanNode limitNode = new PlanNode(Type.LIMIT);
361
362 boolean attach = false;
363 if (limit.offset() != 0) {
364 limitNode.setProperty(Property.LIMIT_OFFSET, limit.offset());
365 attach = true;
366 }
367 if (!limit.isUnlimited()) {
368 limitNode.setProperty(Property.LIMIT_COUNT, limit.rowLimit());
369 attach = true;
370 }
371 if (attach) {
372 limitNode.addLastChild(plan);
373 plan = limitNode;
374 }
375 return plan;
376 }
377
378
379
380
381
382
383
384
385
386
387 protected PlanNode attachProject( QueryContext context,
388 PlanNode plan,
389 List<? extends Column> columns,
390 Map<SelectorName, Table> selectors ) {
391 PlanNode projectNode = new PlanNode(Type.PROJECT);
392
393 List<Column> newColumns = new LinkedList<Column>();
394 List<String> newTypes = new ArrayList<String>();
395 if (columns == null || columns.isEmpty()) {
396
397 for (Map.Entry<SelectorName, Table> entry : selectors.entrySet()) {
398 SelectorName tableName = entry.getKey();
399 Table table = entry.getValue();
400
401 projectNode.addSelector(tableName);
402
403 allColumnsFor(table, tableName, newColumns, newTypes);
404 }
405 } else {
406
407 for (Column column : columns) {
408 SelectorName tableName = column.selectorName();
409
410 projectNode.addSelector(tableName);
411
412
413 Table table = selectors.get(tableName);
414 if (table == null) {
415 context.getProblems().addError(GraphI18n.tableDoesNotExist, tableName);
416 } else {
417
418 String columnName = column.propertyName();
419 if ("*".equals(columnName)) {
420
421 allColumnsFor(table, tableName, newColumns, newTypes);
422 } else {
423
424 newColumns.add(column);
425 org.modeshape.graph.query.validate.Schemata.Column schemaColumn = table.getColumn(columnName);
426 if (schemaColumn != null) {
427 newTypes.add(schemaColumn.getPropertyType());
428 } else {
429 newTypes.add(context.getTypeSystem().getStringFactory().getTypeName());
430 }
431 }
432 boolean validateColumnExistance = context.getHints().validateColumnExistance && !table.hasExtraColumns();
433 if (table.getColumn(columnName) == null && validateColumnExistance && !"*".equals(columnName)) {
434 context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, columnName, tableName);
435 }
436 }
437 }
438 }
439 projectNode.setProperty(Property.PROJECT_COLUMNS, newColumns);
440 projectNode.setProperty(Property.PROJECT_COLUMN_TYPES, newTypes);
441 projectNode.addLastChild(plan);
442 return projectNode;
443 }
444
445 protected void allColumnsFor( Table table,
446 SelectorName tableName,
447 List<Column> columns,
448 List<String> columnTypes ) {
449
450 for (Schemata.Column column : table.getColumns()) {
451 String columnName = column.getName();
452 String propertyName = columnName;
453 Column newColumn = new Column(tableName, propertyName, columnName);
454 columns.add(newColumn);
455 columnTypes.add(column.getPropertyType());
456 }
457 }
458
459
460
461
462
463
464
465
466
467 protected PlanNode attachDuplicateRemoval( QueryContext context,
468 PlanNode plan ) {
469 PlanNode dupNode = new PlanNode(Type.DUP_REMOVE);
470 plan.setParent(dupNode);
471 return dupNode;
472 }
473
474 }