1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.xdbm.search.impl;
21
22
23 import java.io.IOException;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Set;
27
28 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
29 import org.apache.directory.api.ldap.model.cursor.Cursor;
30 import org.apache.directory.api.ldap.model.exception.LdapException;
31 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
32 import org.apache.directory.api.ldap.model.filter.AndNode;
33 import org.apache.directory.api.ldap.model.filter.ApproximateNode;
34 import org.apache.directory.api.ldap.model.filter.AssertionNode;
35 import org.apache.directory.api.ldap.model.filter.BranchNode;
36 import org.apache.directory.api.ldap.model.filter.EqualityNode;
37 import org.apache.directory.api.ldap.model.filter.ExprNode;
38 import org.apache.directory.api.ldap.model.filter.ExtensibleNode;
39 import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
40 import org.apache.directory.api.ldap.model.filter.LeafNode;
41 import org.apache.directory.api.ldap.model.filter.LessEqNode;
42 import org.apache.directory.api.ldap.model.filter.NotNode;
43 import org.apache.directory.api.ldap.model.filter.OrNode;
44 import org.apache.directory.api.ldap.model.filter.PresenceNode;
45 import org.apache.directory.api.ldap.model.filter.ScopeNode;
46 import org.apache.directory.api.ldap.model.filter.SimpleNode;
47 import org.apache.directory.api.ldap.model.filter.SubstringNode;
48 import org.apache.directory.api.util.Strings;
49 import org.apache.directory.server.core.api.partition.Partition;
50 import org.apache.directory.server.core.api.partition.PartitionTxn;
51 import org.apache.directory.server.i18n.I18n;
52 import org.apache.directory.server.xdbm.Index;
53 import org.apache.directory.server.xdbm.IndexNotFoundException;
54 import org.apache.directory.server.xdbm.Store;
55 import org.apache.directory.server.xdbm.search.Optimizer;
56
57
58
59
60
61
62
63 public class DefaultOptimizer implements Optimizer
64 {
65 static final String CANDIDATES_ANNOTATION_KEY = "candidates";
66
67 static final String COUNT_ANNOTATION = "count";
68
69
70 private final Store db;
71 private String contextEntryId;
72
73
74
75
76
77
78
79 public DefaultOptimizer( Store db )
80 {
81 this.db = db;
82 }
83
84
85
86 @SuppressWarnings("PMD.EmptyCatchBlock")
87 private String getContextEntryId( PartitionTxn partitionTxn ) throws LdapException
88 {
89 if ( contextEntryId == null )
90 {
91 try
92 {
93 this.contextEntryId = db.getEntryId( partitionTxn, ( ( Partition ) db ).getSuffixDn() );
94 }
95 catch ( Exception e )
96 {
97
98 }
99 }
100
101 if ( contextEntryId == null )
102 {
103 return Partition.DEFAULT_ID;
104 }
105
106 return contextEntryId;
107 }
108
109
110
111
112
113
114
115
116
117
118 @Override
119 @SuppressWarnings("unchecked")
120 public Long annotate( PartitionTxn partitionTxn, ExprNode node ) throws LdapException
121 {
122
123 Long count = Long.MAX_VALUE;
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 if ( node instanceof ScopeNode )
139 {
140 count = getScopeScan( partitionTxn, ( ScopeNode ) node );
141 }
142 else if ( node instanceof AssertionNode )
143 {
144
145
146
147
148
149 }
150 else if ( node.isLeaf() )
151 {
152 LeafNode leaf = ( LeafNode ) node;
153
154 try
155 {
156 if ( node instanceof PresenceNode )
157 {
158 count = getPresenceScan( partitionTxn, ( PresenceNode ) leaf );
159 }
160 else if ( node instanceof EqualityNode )
161 {
162 count = getEqualityScan( partitionTxn, ( EqualityNode ) leaf );
163 }
164 else if ( node instanceof GreaterEqNode )
165 {
166 count = getGreaterLessScan( partitionTxn, ( GreaterEqNode ) leaf, SimpleNode.EVAL_GREATER );
167 }
168 else if ( node instanceof LessEqNode )
169 {
170 count = getGreaterLessScan( partitionTxn, ( SimpleNode ) leaf, SimpleNode.EVAL_LESSER );
171 }
172 else if ( node instanceof SubstringNode )
173 {
174
175 count = getSubstringScan( partitionTxn, ( SubstringNode ) leaf );
176 }
177 else if ( node instanceof ExtensibleNode )
178 {
179
180 count = getFullScan( partitionTxn, leaf );
181 }
182 else if ( node instanceof ApproximateNode )
183 {
184
185 count = getEqualityScan( partitionTxn, ( ApproximateNode ) leaf );
186 }
187 else
188 {
189 throw new IllegalArgumentException( I18n.err( I18n.ERR_711 ) );
190 }
191 }
192 catch ( IndexNotFoundException | IOException e )
193 {
194 throw new LdapOtherException( e.getMessage(), e );
195 }
196 }
197
198
199
200 else
201 {
202 if ( node instanceof AndNode )
203 {
204 count = getConjunctionScan( partitionTxn, ( AndNode ) node );
205 }
206 else if ( node instanceof OrNode )
207 {
208 count = getDisjunctionScan( partitionTxn, ( OrNode ) node );
209 }
210 else if ( node instanceof NotNode )
211 {
212 annotate( partitionTxn, ( ( NotNode ) node ).getFirstChild() );
213
214
215
216
217
218
219
220 count = Long.MAX_VALUE;
221 }
222 else
223 {
224 throw new IllegalArgumentException( I18n.err( I18n.ERR_712 ) );
225 }
226 }
227
228
229 if ( count < 0L )
230 {
231 count = Long.MAX_VALUE;
232 }
233
234 node.set( COUNT_ANNOTATION, count );
235
236 return count;
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251 private long getConjunctionScan( PartitionTxn partitionTxn, BranchNode node ) throws LdapException
252 {
253 long count = Long.MAX_VALUE;
254 List<ExprNode> children = node.getChildren();
255
256 for ( ExprNode child : children )
257 {
258 if ( ( count == 1 ) && ( child instanceof ScopeNode ) )
259 {
260
261 break;
262 }
263
264 annotate( partitionTxn, child );
265 count = Math.min( ( ( Long ) child.get( COUNT_ANNOTATION ) ), count );
266
267 if ( count == 0 )
268 {
269
270 break;
271 }
272 }
273
274 return count;
275 }
276
277
278
279
280
281
282
283
284
285
286
287 private long getDisjunctionScan( PartitionTxn partitionTxn, BranchNode node ) throws LdapException
288 {
289 List<ExprNode> children = node.getChildren();
290 long total = 0L;
291
292 for ( ExprNode child : children )
293 {
294 annotate( partitionTxn, child );
295 total += ( Long ) child.get( COUNT_ANNOTATION );
296
297 if ( total == Long.MAX_VALUE )
298 {
299
300 break;
301 }
302 }
303
304 return total;
305 }
306
307
308
309
310
311
312
313
314
315
316 @SuppressWarnings("unchecked")
317 private <V> long getEqualityScan( PartitionTxn partitionTxn, SimpleNode<V> node ) throws LdapException, IndexNotFoundException, IOException
318 {
319 if ( db.hasIndexOn( node.getAttributeType() ) )
320 {
321 Index<V, String> idx = ( Index<V, String> ) db.getIndex( node.getAttributeType() );
322
323 String normalizedKey;
324
325 if ( node.getValue().isSchemaAware() )
326 {
327 normalizedKey = node.getValue().getNormalized();
328 }
329 else
330 {
331 normalizedKey = node.getAttributeType().getEquality().getNormalizer().normalize( node.getValue().getString() );
332 }
333
334 Cursor<String> result = idx.forwardValueCursor( partitionTxn, ( V ) normalizedKey );
335 Set<String> values = new HashSet<>();
336 int nbFound = 0;
337
338 for ( String value : result )
339 {
340 values.add( value );
341 nbFound++;
342
343
344 if ( nbFound == 100 )
345 {
346 break;
347 }
348 }
349
350 result.close();
351
352 if ( nbFound < 100 )
353 {
354
355 node.set( CANDIDATES_ANNOTATION_KEY, values );
356
357 return values.size();
358 }
359 else
360 {
361
362 node.set( CANDIDATES_ANNOTATION_KEY, null );
363
364 return idx.count( partitionTxn, ( V ) node.getValue().getNormalized() );
365 }
366 }
367
368
369 return Long.MAX_VALUE;
370 }
371
372
373
374
375
376
377
378
379
380
381
382 @SuppressWarnings("unchecked")
383 private <V> long getGreaterLessScan( PartitionTxn partitionTxn, SimpleNode<V> node, boolean isGreaterThan ) throws LdapException, IndexNotFoundException
384 {
385 if ( db.hasIndexOn( node.getAttributeType() ) )
386 {
387 Index<V, String> idx = ( Index<V, String> ) db.getIndex( node.getAttributeType() );
388
389 if ( isGreaterThan )
390 {
391 return idx.greaterThanCount( partitionTxn, ( V ) node.getValue().getString() );
392 }
393 else
394 {
395 return idx.lessThanCount( partitionTxn, ( V ) node.getValue().getString() );
396 }
397 }
398
399
400 return Long.MAX_VALUE;
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414 private long getSubstringScan( PartitionTxn partitionTxn, SubstringNode node ) throws LdapException, IndexNotFoundException
415 {
416 if ( db.hasIndexOn( node.getAttributeType() ) )
417 {
418 Index<String, String> idx = ( Index<String, String> ) db.getIndex( node.getAttributeType() );
419
420 String initial = node.getInitial();
421
422 if ( Strings.isEmpty( initial ) )
423 {
424
425 return idx.count( partitionTxn );
426 }
427 else
428 {
429 return idx.greaterThanCount( partitionTxn, initial );
430 }
431 }
432 else
433 {
434
435 return Long.MAX_VALUE;
436 }
437 }
438
439
440
441
442
443
444
445
446
447
448
449 private long getFullScan( PartitionTxn partitionTxn, LeafNode node ) throws LdapException, IndexNotFoundException
450 {
451 if ( db.hasIndexOn( node.getAttributeType() ) )
452 {
453 Index<?, ?> idx = db.getIndex( node.getAttributeType() );
454 return idx.count( partitionTxn );
455 }
456
457 return Long.MAX_VALUE;
458 }
459
460
461
462
463
464
465
466
467
468
469 private long getPresenceScan( PartitionTxn partitionTxn, PresenceNode node ) throws LdapException
470 {
471 if ( db.hasUserIndexOn( node.getAttributeType() )
472 || node.getAttributeType().getOid().equals( SchemaConstants.ADMINISTRATIVE_ROLE_AT_OID ) )
473 {
474 Index<String, String> presenceIndex = db.getPresenceIndex();
475
476 return presenceIndex.count( partitionTxn, node.getAttributeType().getOid() );
477 }
478 else if ( db.hasSystemIndexOn( node.getAttributeType() )
479 || ( node.getAttributeType().getOid() == SchemaConstants.ENTRY_UUID_AT_OID ) )
480 {
481
482
483 return db.count( partitionTxn );
484 }
485
486 return Long.MAX_VALUE;
487 }
488
489
490
491
492
493
494
495
496
497 private long getScopeScan( PartitionTxn partitionTxn, ScopeNode node ) throws LdapException
498 {
499 String id = node.getBaseId();
500
501 switch ( node.getScope() )
502 {
503 case OBJECT:
504 return 1L;
505
506 case ONELEVEL:
507 return db.getChildCount( partitionTxn, id );
508
509 case SUBTREE:
510 if ( id == getContextEntryId( partitionTxn ) )
511 {
512 return db.count( partitionTxn );
513 }
514 else
515 {
516 return db.getRdnIndex().reverseLookup( partitionTxn, id ).getNbDescendants() + 1L;
517 }
518
519 default:
520 throw new IllegalArgumentException( I18n.err( I18n.ERR_713 ) );
521 }
522 }
523 }