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.List;
25 import java.util.Set;
26 import java.util.regex.Pattern;
27
28 import org.apache.directory.api.ldap.model.cursor.Cursor;
29 import org.apache.directory.api.ldap.model.cursor.CursorException;
30 import org.apache.directory.api.ldap.model.entry.Value;
31 import org.apache.directory.api.ldap.model.exception.LdapException;
32 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
33 import org.apache.directory.api.ldap.model.filter.AndNode;
34 import org.apache.directory.api.ldap.model.filter.ApproximateNode;
35 import org.apache.directory.api.ldap.model.filter.EqualityNode;
36 import org.apache.directory.api.ldap.model.filter.ExprNode;
37 import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
38 import org.apache.directory.api.ldap.model.filter.LessEqNode;
39 import org.apache.directory.api.ldap.model.filter.NotNode;
40 import org.apache.directory.api.ldap.model.filter.OrNode;
41 import org.apache.directory.api.ldap.model.filter.PresenceNode;
42 import org.apache.directory.api.ldap.model.filter.ScopeNode;
43 import org.apache.directory.api.ldap.model.filter.SubstringNode;
44 import org.apache.directory.api.ldap.model.message.SearchScope;
45 import org.apache.directory.api.ldap.model.name.Dn;
46 import org.apache.directory.api.ldap.model.name.Rdn;
47 import org.apache.directory.api.ldap.model.schema.AttributeType;
48 import org.apache.directory.api.ldap.model.schema.MatchingRule;
49 import org.apache.directory.api.ldap.model.schema.Normalizer;
50 import org.apache.directory.api.ldap.model.schema.PrepareString;
51 import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer;
52 import org.apache.directory.api.util.exception.NotImplementedException;
53 import org.apache.directory.server.core.api.partition.Partition;
54 import org.apache.directory.server.core.api.partition.PartitionTxn;
55 import org.apache.directory.server.i18n.I18n;
56 import org.apache.directory.server.xdbm.Index;
57 import org.apache.directory.server.xdbm.IndexEntry;
58 import org.apache.directory.server.xdbm.IndexNotFoundException;
59 import org.apache.directory.server.xdbm.ParentIdAndRdn;
60 import org.apache.directory.server.xdbm.SingletonIndexCursor;
61 import org.apache.directory.server.xdbm.Store;
62 import org.apache.directory.server.xdbm.search.PartitionSearchResult;
63 import org.apache.directory.server.xdbm.search.cursor.ApproximateCursor;
64 import org.apache.directory.server.xdbm.search.cursor.ChildrenCursor;
65 import org.apache.directory.server.xdbm.search.cursor.DescendantCursor;
66 import org.apache.directory.server.xdbm.search.evaluator.ApproximateEvaluator;
67
68
69
70
71
72
73
74 public class CursorBuilder
75 {
76
77 private Store db = null;
78
79
80 private EvaluatorBuilder evaluatorBuilder;
81
82
83
84
85
86
87
88
89 public CursorBuilder( Store db, EvaluatorBuilder evaluatorBuilder )
90 {
91 this.db = db;
92 this.evaluatorBuilder = evaluatorBuilder;
93 }
94
95
96 public <T> long build( PartitionTxn partitionTxn, ExprNode node, PartitionSearchResult searchResult ) throws LdapException
97 {
98 Object count = node.get( DefaultOptimizer.COUNT_ANNOTATION );
99
100 if ( ( count != null ) && ( ( Long ) count ) == 0L )
101 {
102 return 0;
103 }
104
105 try
106 {
107 switch ( node.getAssertionType() )
108 {
109
110
111 case APPROXIMATE:
112 return computeApproximate( partitionTxn, ( ApproximateNode<T> ) node, searchResult );
113
114 case EQUALITY:
115 return computeEquality( partitionTxn, ( EqualityNode<T> ) node, searchResult );
116
117 case GREATEREQ:
118 return computeGreaterEq( partitionTxn, ( GreaterEqNode<T> ) node, searchResult );
119
120 case LESSEQ:
121 return computeLessEq( partitionTxn, ( LessEqNode<T> ) node, searchResult );
122
123 case PRESENCE:
124 return computePresence( partitionTxn, ( PresenceNode ) node, searchResult );
125
126 case SCOPE:
127 if ( ( ( ScopeNode ) node ).getScope() == SearchScope.ONELEVEL )
128 {
129 return computeOneLevelScope( partitionTxn, ( ScopeNode ) node, searchResult );
130 }
131 else
132 {
133 return computeSubLevelScope( partitionTxn, ( ScopeNode ) node, searchResult );
134 }
135
136 case SUBSTRING:
137 return computeSubstring( partitionTxn, ( SubstringNode ) node, searchResult );
138
139
140
141 case AND:
142 return computeAnd( partitionTxn, ( AndNode ) node, searchResult );
143
144 case NOT:
145
146 return computeNot( ( NotNode ) node, searchResult );
147
148 case OR:
149 return computeOr( partitionTxn, ( OrNode ) node, searchResult );
150
151
152
153 case ASSERTION:
154 case EXTENSIBLE:
155 throw new NotImplementedException();
156
157 default:
158 throw new IllegalStateException( I18n.err( I18n.ERR_260, node.getAssertionType() ) );
159 }
160 }
161 catch ( IndexNotFoundException | CursorException | IOException e )
162 {
163 throw new LdapOtherException( e.getMessage(), e );
164 }
165 }
166
167
168
169
170
171
172
173 private <T> long computeApproximate( PartitionTxn partitionTxn, ApproximateNode<T> node, PartitionSearchResult searchResult )
174 throws LdapException, IndexNotFoundException, CursorException, IOException
175 {
176 ApproximateCursor<T> cursor = new ApproximateCursor<>( partitionTxn, db,
177 ( ApproximateEvaluator<T> ) evaluatorBuilder
178 .build( partitionTxn, node ) );
179
180 int nbResults = 0;
181 Set<String> uuidSet = searchResult.getCandidateSet();
182
183 while ( cursor.next() )
184 {
185 IndexEntry<T, String> indexEntry = cursor.get();
186
187 String uuid = indexEntry.getId();
188 boolean added = uuidSet.add( uuid );
189
190
191 if ( added )
192 {
193 nbResults++;
194 }
195 }
196
197 cursor.close();
198
199 return nbResults;
200 }
201
202
203
204
205
206
207 private <T> long computeEquality( PartitionTxn partitionTxn, EqualityNode<T> node, PartitionSearchResult searchResult )
208 throws LdapException, IndexNotFoundException, CursorException, IOException
209 {
210 Set<String> thisCandidates = ( Set<String> ) node.get( DefaultOptimizer.CANDIDATES_ANNOTATION_KEY );
211
212 if ( thisCandidates != null )
213 {
214 Set<String> candidates = searchResult.getCandidateSet();
215
216 for ( String candidate : thisCandidates )
217 {
218 candidates.add( candidate );
219 }
220
221 return thisCandidates.size();
222 }
223
224 AttributeType attributeType = node.getAttributeType();
225 Value value = node.getValue();
226 int nbResults = 0;
227
228
229 if ( db.hasIndexOn( attributeType ) )
230 {
231
232 Index<T, String> userIndex = ( Index<T, String> ) db.getIndex( attributeType );
233 Cursor<IndexEntry<T, String>> userIdxCursor = userIndex.forwardCursor( partitionTxn, ( T ) value.getNormalized() );
234 Set<String> uuidSet = searchResult.getCandidateSet();
235
236
237 while ( userIdxCursor.next() )
238 {
239 IndexEntry<T, String> indexEntry = userIdxCursor.get();
240
241 String uuid = indexEntry.getId();
242 boolean added = uuidSet.add( uuid );
243
244
245 if ( added )
246 {
247 nbResults++;
248 }
249 }
250
251 userIdxCursor.close();
252 }
253 else
254 {
255
256 return Long.MAX_VALUE;
257 }
258
259 return nbResults;
260 }
261
262
263
264
265
266
267 private <T> long computeGreaterEq( PartitionTxn partitionTxn, GreaterEqNode<T> node, PartitionSearchResult searchResult )
268 throws LdapException, IndexNotFoundException, CursorException, IOException
269 {
270 AttributeType attributeType = node.getAttributeType();
271 Value value = node.getValue();
272 int nbResults = 0;
273
274
275 if ( db.hasIndexOn( attributeType ) )
276 {
277
278 Index<T, String> userIndex = ( Index<T, String> ) db.getIndex( attributeType );
279 Cursor<IndexEntry<T, String>> userIdxCursor = userIndex.forwardCursor( partitionTxn );
280
281
282 IndexEntry<T, String> indexEntry = new IndexEntry<>();
283 indexEntry.setKey( ( T ) value.getString() );
284
285 userIdxCursor.before( indexEntry );
286 Set<String> uuidSet = searchResult.getCandidateSet();
287
288
289 while ( userIdxCursor.next() )
290 {
291 indexEntry = userIdxCursor.get();
292
293 String uuid = indexEntry.getId();
294 boolean added = uuidSet.add( uuid );
295
296
297 if ( added )
298 {
299 nbResults++;
300 }
301 }
302
303 userIdxCursor.close();
304 }
305 else
306 {
307
308 return Long.MAX_VALUE;
309 }
310
311 return nbResults;
312 }
313
314
315
316
317
318
319 private <T> long computeLessEq( PartitionTxn partitionTxn, LessEqNode<T> node, PartitionSearchResult searchResult )
320 throws LdapException, IndexNotFoundException, CursorException, IOException
321 {
322 AttributeType attributeType = node.getAttributeType();
323 Value value = node.getValue();
324 int nbResults = 0;
325
326
327 if ( db.hasIndexOn( attributeType ) )
328 {
329
330 Index<T, String> userIndex = ( Index<T, String> ) db.getIndex( attributeType );
331 Cursor<IndexEntry<T, String>> userIdxCursor = userIndex.forwardCursor( partitionTxn );
332
333
334 IndexEntry<T, String> indexEntry = new IndexEntry<>();
335 indexEntry.setKey( ( T ) value.getString() );
336
337 userIdxCursor.after( indexEntry );
338 Set<String> uuidSet = searchResult.getCandidateSet();
339
340
341 while ( userIdxCursor.previous() )
342 {
343 indexEntry = userIdxCursor.get();
344
345 String uuid = indexEntry.getId();
346 boolean added = uuidSet.add( uuid );
347
348
349 if ( added )
350 {
351 nbResults++;
352 }
353 }
354
355 userIdxCursor.close();
356 }
357 else
358 {
359
360 return Long.MAX_VALUE;
361 }
362
363 return nbResults;
364 }
365
366
367
368
369
370
371 private long computePresence( PartitionTxn partitionTxn, PresenceNode node, PartitionSearchResult searchResult )
372 throws LdapException, CursorException, IOException
373 {
374 AttributeType attributeType = node.getAttributeType();
375 int nbResults = 0;
376
377
378 if ( db.hasIndexOn( attributeType ) )
379 {
380
381 Cursor<IndexEntry<String, String>> presenceCursor = db.getPresenceIndex().forwardCursor(
382 partitionTxn, attributeType.getOid() );
383
384
385 Set<String> uuidSet = searchResult.getCandidateSet();
386
387
388 while ( presenceCursor.next() )
389 {
390 IndexEntry<String, String> indexEntry = presenceCursor.get();
391
392 String uuid = indexEntry.getId();
393 boolean added = uuidSet.add( uuid );
394
395
396 if ( added )
397 {
398 nbResults++;
399 }
400 }
401
402 presenceCursor.close();
403 }
404 else
405 {
406
407 return Long.MAX_VALUE;
408 }
409
410 return nbResults;
411 }
412
413
414
415
416
417
418 private long computeOneLevelScope( PartitionTxn partitionTxn, ScopeNode node, PartitionSearchResult searchResult )
419 throws LdapException, CursorException, IOException
420 {
421 int nbResults = 0;
422
423
424
425 Cursor<IndexEntry<ParentIdAndRdn, String>> rdnCursor = db.getRdnIndex().forwardCursor( partitionTxn );
426
427 IndexEntry<ParentIdAndRdn, String> startingPos = new IndexEntry<>();
428 startingPos.setKey( new ParentIdAndRdn( node.getBaseId(), ( Rdn[] ) null ) );
429 rdnCursor.before( startingPos );
430
431 Cursor<IndexEntry<String, String>> scopeCursor = new ChildrenCursor( partitionTxn, db, node.getBaseId(), rdnCursor );
432 Set<String> candidateSet = searchResult.getCandidateSet();
433
434
435
436 while ( scopeCursor.next() )
437 {
438 IndexEntry<String, String> indexEntry = scopeCursor.get();
439
440 String uuid = indexEntry.getId();
441
442
443
444 if ( searchResult.isDerefAlways() || searchResult.isDerefInSearching() )
445 {
446 Dn aliasedDn = db.getAliasIndex().reverseLookup( partitionTxn, uuid );
447
448 if ( aliasedDn != null )
449 {
450 if ( !aliasedDn.isSchemaAware() )
451 {
452 aliasedDn = new Dn( evaluatorBuilder.getSchemaManager(), aliasedDn );
453 }
454
455 String aliasedId = db.getEntryId( partitionTxn, aliasedDn );
456
457
458
459 boolean added = candidateSet.add( aliasedId );
460
461 if ( added )
462 {
463 nbResults++;
464 }
465 }
466 else
467 {
468
469 boolean added = candidateSet.add( uuid );
470
471
472 if ( added )
473 {
474 nbResults++;
475 }
476 }
477 }
478 else
479 {
480
481 boolean added = candidateSet.add( uuid );
482
483
484 if ( added )
485 {
486 nbResults++;
487 }
488 }
489 }
490
491 scopeCursor.close();
492
493 return nbResults;
494 }
495
496
497
498
499
500
501 private long computeSubLevelScope( PartitionTxn partitionTxn, ScopeNode node, PartitionSearchResult searchResult )
502 throws LdapException, IOException, CursorException
503 {
504
505 String contextEntryId = db.getEntryId( partitionTxn, ( ( Partition ) db ).getSuffixDn() );
506
507 if ( node.getBaseId() == contextEntryId )
508 {
509 return Long.MAX_VALUE;
510 }
511
512 int nbResults = 0;
513
514
515
516 String baseId = node.getBaseId();
517 ParentIdAndRdn parentIdAndRdn = db.getRdnIndex().reverseLookup( partitionTxn, baseId );
518 IndexEntry<ParentIdAndRdn, String> startingPos = new IndexEntry<>();
519
520 startingPos.setKey( parentIdAndRdn );
521 startingPos.setId( baseId );
522
523 Cursor<IndexEntry<ParentIdAndRdn, String>> rdnCursor = new SingletonIndexCursor<>( partitionTxn,
524 startingPos );
525 String parentId = parentIdAndRdn.getParentId();
526
527 Cursor<IndexEntry<String, String>> scopeCursor = new DescendantCursor( partitionTxn, db, baseId, parentId, rdnCursor );
528 Set<String> candidateSet = searchResult.getCandidateSet();
529
530
531
532 while ( scopeCursor.next() )
533 {
534 IndexEntry<String, String> indexEntry = scopeCursor.get();
535
536 String uuid = indexEntry.getId();
537
538
539
540 if ( searchResult.isDerefAlways() || searchResult.isDerefInSearching() )
541 {
542 Dn aliasedDn = db.getAliasIndex().reverseLookup( partitionTxn, uuid );
543
544 if ( aliasedDn != null )
545 {
546 if ( !aliasedDn.isSchemaAware() )
547 {
548 aliasedDn = new Dn( evaluatorBuilder.getSchemaManager(), aliasedDn );
549 }
550
551 String aliasedId = db.getEntryId( partitionTxn, aliasedDn );
552
553
554
555 boolean added = candidateSet.add( aliasedId );
556
557 if ( added )
558 {
559 nbResults++;
560
561 ScopeNode newScopeNode = new ScopeNode(
562 node.getDerefAliases(),
563 aliasedDn,
564 aliasedId,
565 node.getScope() );
566
567 nbResults += computeSubLevelScope( partitionTxn, newScopeNode, searchResult );
568 }
569 }
570 else
571 {
572
573
574 boolean added = candidateSet.add( uuid );
575
576 if ( added )
577 {
578 nbResults++;
579 }
580 }
581 }
582 else
583 {
584
585 boolean added = candidateSet.add( uuid );
586
587 if ( added )
588 {
589 nbResults++;
590 }
591 }
592 }
593
594 scopeCursor.close();
595
596 return nbResults;
597 }
598
599
600
601
602
603
604 private long computeSubstring( PartitionTxn partitionTxn, SubstringNode node, PartitionSearchResult searchResult )
605 throws LdapException, IndexNotFoundException, CursorException, IOException
606 {
607 AttributeType attributeType = node.getAttributeType();
608
609
610 if ( attributeType.getSubstring() == null )
611 {
612
613 return 0L;
614 }
615
616
617 if ( db.hasIndexOn( attributeType ) )
618 {
619 Index<String, String> userIndex = ( Index<String, String> ) db.getIndex( attributeType );
620 Cursor<IndexEntry<String, String>> cursor = userIndex.forwardCursor( partitionTxn );
621
622
623 IndexEntry<String, String> indexEntry = new IndexEntry<>();
624 String initial = node.getInitial();
625
626 boolean fullIndexScan = false;
627
628 if ( initial == null )
629 {
630 fullIndexScan = true;
631 cursor.beforeFirst();
632 }
633 else
634 {
635 indexEntry.setKey( attributeType.getEquality().getNormalizer().normalize( initial, PrepareString.AssertionType.SUBSTRING_INITIAL ) );
636
637 cursor.before( indexEntry );
638 }
639
640 int nbResults = 0;
641
642 MatchingRule rule = attributeType.getSubstring();
643
644 if ( rule == null )
645 {
646 rule = attributeType.getEquality();
647 }
648
649 Normalizer normalizer;
650 Pattern regexp;
651
652 if ( rule != null )
653 {
654 normalizer = rule.getNormalizer();
655 }
656 else
657 {
658 normalizer = new NoOpNormalizer( attributeType.getSyntaxOid() );
659 }
660
661
662
663 if ( attributeType.getSyntax().isHumanReadable() )
664 {
665 regexp = node.getRegex( normalizer );
666 }
667 else
668 {
669 regexp = null;
670 }
671
672 Set<String> uuidSet = searchResult.getCandidateSet();
673
674 if ( regexp == null )
675 {
676 return nbResults;
677 }
678
679
680 while ( cursor.next() )
681 {
682 indexEntry = cursor.get();
683
684 String key = indexEntry.getKey();
685
686 boolean matched = regexp.matcher( key ).matches();
687
688 if ( !fullIndexScan && !matched )
689 {
690 cursor.close();
691
692 return nbResults;
693 }
694
695 if ( !matched )
696 {
697 continue;
698 }
699
700 String uuid = indexEntry.getId();
701
702 boolean added = uuidSet.add( uuid );
703
704
705 if ( added )
706 {
707 nbResults++;
708 }
709 }
710
711 cursor.close();
712
713 return nbResults;
714 }
715 else
716 {
717
718 return Long.MAX_VALUE;
719 }
720 }
721
722
723
724
725
726
727
728
729
730 private long computeOr( PartitionTxn partitionTxn, OrNode node, PartitionSearchResult searchResult )
731 throws LdapException
732 {
733 List<ExprNode> children = node.getChildren();
734
735 long nbOrResults = 0;
736
737
738 for ( ExprNode child : children )
739 {
740 Object count = child.get( DefaultOptimizer.COUNT_ANNOTATION );
741
742 if ( count != null )
743 {
744 long countLong = ( Long ) count;
745
746 if ( countLong == 0 )
747 {
748
749 continue;
750 }
751 else if ( countLong == Long.MAX_VALUE )
752 {
753
754 return countLong;
755 }
756 }
757
758 long nbResults = build( partitionTxn, child, searchResult );
759
760 if ( nbResults == Long.MAX_VALUE )
761 {
762
763 return nbResults;
764 }
765 else
766 {
767 nbOrResults += nbResults;
768 }
769 }
770
771 return nbOrResults;
772 }
773
774
775
776
777
778
779
780
781
782 private long computeAnd( PartitionTxn partitionTxn, AndNode node, PartitionSearchResult searchResult )
783 throws LdapException
784 {
785 int minIndex = 0;
786 long minValue = Long.MAX_VALUE;
787 long value;
788
789
790
791
792
793
794 final List<ExprNode> children = node.getChildren();
795
796 for ( int i = 0; i < children.size(); i++ )
797 {
798 ExprNode child = children.get( i );
799 Object count = child.get( DefaultOptimizer.COUNT_ANNOTATION );
800
801 if ( count == null )
802 {
803 continue;
804 }
805
806 value = ( Long ) count;
807
808 if ( value == 0L )
809 {
810
811 return 0L;
812 }
813
814 if ( value < minValue )
815 {
816 minValue = value;
817 minIndex = i;
818 }
819 }
820
821
822 ExprNode minChild = children.get( minIndex );
823
824 return build( partitionTxn, minChild, searchResult );
825 }
826
827
828
829
830
831
832
833
834
835 private long computeNot( NotNode node, PartitionSearchResult searchResult )
836 {
837 final List<ExprNode> children = node.getChildren();
838
839 ExprNode child = children.get( 0 );
840 Object count = child.get( DefaultOptimizer.COUNT_ANNOTATION );
841
842 if ( count == null )
843 {
844 return Long.MAX_VALUE;
845 }
846
847 long value = ( Long ) count;
848
849 if ( value == Long.MAX_VALUE )
850 {
851
852 return 0L;
853 }
854
855 return Long.MAX_VALUE;
856 }
857 }