2 * Copyright 2001-2014, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
4 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
5 * This file may be used under the terms of the MIT License.
7 #ifndef _FILE_SYSTEMS_QUERY_PARSER_H
8 #define _FILE_SYSTEMS_QUERY_PARSER_H
11 /*! Query parsing and evaluation
13 The pattern matching is roughly based on code originally written
14 by J. Kercheval, and on code written by Kenneth Almquist, though
19 // The parser has a very static design, but it will do what is required.
21 // ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator
22 // precedence, that is =,!=,>,<,>=,<= .. && .. ||.
23 // Apparently, the "!" (not) can only be used with brackets.
25 // If you think that there are too few NULL pointer checks in some places
26 // of the code, just read the beginning of the query constructor.
27 // The API is not fully available, just the Query and the Expression class
33 # include "fssh_api_wrapper.h"
34 # include "fssh_auto_deleter.h"
43 # include <fs_interface.h>
44 # include <fs_query.h>
45 # include <TypeConstants.h>
47 # include <util/SinglyLinkedList.h>
48 # include <util/Stack.h>
50 # include <query_private.h>
55 #include <file_systems/QueryParserUtils.h>
60 #ifndef QUERY_RETURN_ERROR
61 # define QUERY_RETURN_ERROR(error) return (error)
64 #ifndef QUERY_REPORT_ERROR
65 # define QUERY_REPORT_ERROR(error) do {} while (false)
69 # define QUERY_FATAL(message...) panic(message)
73 # define QUERY_INFORM(message...) dprintf(message)
77 # define QUERY_D(block)
81 namespace QueryParser
{
84 template<typename QueryPolicy
> class Equation
;
85 template<typename QueryPolicy
> class Expression
;
86 template<typename QueryPolicy
> class Term
;
87 template<typename QueryPolicy
> class Query
;
97 // is only used for invalid equations
103 OP_GREATER_THAN_OR_EQUAL
,
104 OP_LESS_THAN_OR_EQUAL
,
108 template<typename QueryPolicy
>
116 char String
[QueryPolicy::kMaxFileNameLength
];
120 template<typename QueryPolicy
>
123 typedef typename
QueryPolicy::Entry Entry
;
124 typedef typename
QueryPolicy::Index Index
;
125 typedef typename
QueryPolicy::IndexIterator IndexIterator
;
126 typedef typename
QueryPolicy::Node Node
;
127 typedef typename
QueryPolicy::Context Context
;
130 Query(Context
* context
,
131 Expression
<QueryPolicy
>* expression
,
132 uint32 flags
, port_id port
, uint32 token
);
135 static status_t
Create(Context
* context
, const char* queryString
,
136 uint32 flags
, port_id port
, uint32 token
,
137 Query
<QueryPolicy
>*& _query
);
140 inline status_t
GetNextEntry(struct dirent
* dirent
, size_t size
);
142 void LiveUpdate(Entry
* entry
, Node
* node
,
143 const char* attribute
, int32 type
,
144 const uint8
* oldKey
, size_t oldLength
,
145 const uint8
* newKey
, size_t newLength
);
146 void LiveUpdateRenameMove(Entry
* entry
, Node
* node
,
147 ino_t oldDirectoryID
, const char* oldName
,
148 size_t oldLength
, ino_t newDirectoryID
,
149 const char* newName
, size_t newLength
);
151 Expression
<QueryPolicy
>* GetExpression() const
152 { return fExpression
; }
158 status_t
_GetNextEntry(struct dirent
* dirent
, size_t size
);
159 void _SendEntryNotification(Entry
* entry
,
160 status_t (*notify
)(port_id
, int32
, dev_t
, ino_t
,
161 const char*, ino_t
));
165 Expression
<QueryPolicy
>* fExpression
;
166 Equation
<QueryPolicy
>* fCurrent
;
167 IndexIterator
* fIterator
;
169 Stack
<Equation
<QueryPolicy
>*> fStack
;
178 /*! Abstract base class for the operator/equation classes.
180 template<typename QueryPolicy
>
183 typedef typename
QueryPolicy::Entry Entry
;
184 typedef typename
QueryPolicy::Index Index
;
185 typedef typename
QueryPolicy::IndexIterator IndexIterator
;
186 typedef typename
QueryPolicy::Node Node
;
187 typedef typename
QueryPolicy::Context Context
;
190 Term(int8 op
) : fOp(op
), fParent(NULL
) {}
193 int8
Op() const { return fOp
; }
195 void SetParent(Term
<QueryPolicy
>* parent
)
196 { fParent
= parent
; }
197 Term
<QueryPolicy
>* Parent() const { return fParent
; }
199 virtual status_t
Match(Entry
* entry
, Node
* node
,
200 const char* attribute
= NULL
, int32 type
= 0,
201 const uint8
* key
= NULL
, size_t size
= 0) = 0;
202 virtual void Complement() = 0;
204 virtual void CalculateScore(Index
& index
) = 0;
205 virtual int32
Score() const = 0;
207 virtual status_t
InitCheck() = 0;
209 virtual bool NeedsEntry() = 0;
212 virtual void PrintToStream() = 0;
217 Term
<QueryPolicy
>* fParent
;
221 /*! An Equation object represents an "attribute-equation operator-value" pair.
223 Although an Equation object is quite independent from the volume on which
224 the query is run, there are some dependencies that are produced while
226 The type/size of the value, the score, and if it has an index or not.
227 So you could run more than one query on the same volume, but it might return
228 wrong values when it runs concurrently on another volume.
229 That's not an issue right now, because we run single-threaded and don't use
230 queries more than once.
232 template<typename QueryPolicy
>
233 class Equation
: public Term
<QueryPolicy
> {
235 typedef typename
QueryPolicy::Entry Entry
;
236 typedef typename
QueryPolicy::Index Index
;
237 typedef typename
QueryPolicy::IndexIterator IndexIterator
;
238 typedef typename
QueryPolicy::Node Node
;
239 typedef typename
QueryPolicy::Context Context
;
242 Equation(char** expression
);
245 virtual status_t
InitCheck();
247 status_t
ParseQuotedString(char** _start
, char** _end
);
248 char* CopyString(char* start
, char* end
);
250 virtual status_t
Match(Entry
* entry
, Node
* node
,
251 const char* attribute
= NULL
, int32 type
= 0,
252 const uint8
* key
= NULL
, size_t size
= 0);
253 virtual void Complement();
255 status_t
PrepareQuery(Context
* context
, Index
& index
,
256 IndexIterator
** iterator
, bool queryNonIndexed
);
257 status_t
GetNextMatching(Context
* context
,
258 IndexIterator
* iterator
, struct dirent
* dirent
,
261 virtual void CalculateScore(Index
&index
);
262 virtual int32
Score() const { return fScore
; }
264 virtual bool NeedsEntry();
267 virtual void PrintToStream();
271 Equation(const Equation
& other
);
272 Equation
& operator=(const Equation
& other
);
275 status_t
ConvertValue(type_code type
);
276 bool CompareTo(const uint8
* value
, size_t size
);
277 uint8
* Value() const { return (uint8
*)&fValue
; }
278 status_t
MatchEmptyString();
282 union value
<QueryPolicy
> fValue
;
292 /*! The Operator class does not represent a generic operator, but only those
293 that combine two equations, namely "or", and "and".
295 template<typename QueryPolicy
>
296 class Operator
: public Term
<QueryPolicy
> {
298 typedef typename
QueryPolicy::Entry Entry
;
299 typedef typename
QueryPolicy::Index Index
;
300 typedef typename
QueryPolicy::IndexIterator IndexIterator
;
301 typedef typename
QueryPolicy::Node Node
;
302 typedef typename
QueryPolicy::Context Context
;
305 Operator(Term
<QueryPolicy
>* left
, int8 op
,
306 Term
<QueryPolicy
>* right
);
309 Term
<QueryPolicy
>* Left() const { return fLeft
; }
310 Term
<QueryPolicy
>* Right() const { return fRight
; }
312 virtual status_t
Match(Entry
* entry
, Node
* node
,
313 const char* attribute
= NULL
, int32 type
= 0,
314 const uint8
* key
= NULL
, size_t size
= 0);
315 virtual void Complement();
317 virtual void CalculateScore(Index
& index
);
318 virtual int32
Score() const;
320 virtual status_t
InitCheck();
322 virtual bool NeedsEntry();
325 virtual void PrintToStream();
329 Operator(const Operator
& other
);
330 Operator
& operator=(const Operator
& other
);
333 Term
<QueryPolicy
>* fLeft
;
334 Term
<QueryPolicy
>* fRight
;
338 template<typename QueryPolicy
>
341 typedef typename
QueryPolicy::Entry Entry
;
342 typedef typename
QueryPolicy::Index Index
;
343 typedef typename
QueryPolicy::IndexIterator IndexIterator
;
344 typedef typename
QueryPolicy::Node Node
;
345 typedef typename
QueryPolicy::Context Context
;
348 Expression(char* expr
);
351 status_t
InitCheck();
352 const char* Position() const { return fPosition
; }
353 Term
<QueryPolicy
>* Root() const { return fTerm
; }
356 Term
<QueryPolicy
>* ParseOr(char** expr
);
357 Term
<QueryPolicy
>* ParseAnd(char** expr
);
358 Term
<QueryPolicy
>* ParseEquation(char** expr
);
360 bool IsOperator(char** expr
, char op
);
363 Expression(const Expression
& other
);
364 Expression
& operator=(const Expression
& other
);
368 Term
<QueryPolicy
>* fTerm
;
375 template<typename QueryPolicy
>
376 Equation
<QueryPolicy
>::Equation(char** expr
)
378 Term
<QueryPolicy
>(OP_EQUATION
),
384 char* string
= *expr
;
385 char* start
= string
;
388 // Since the equation is the integral part of any query, we're just parsing
389 // the whole thing here.
390 // The whitespace at the start is already removed in
391 // Expression::ParseEquation()
393 if (*start
== '"' || *start
== '\'') {
394 // string is quoted (start has to be on the beginning of a string)
395 if (ParseQuotedString(&start
, &end
) < B_OK
)
398 // set string to a valid start of the equation symbol
400 skipWhitespace(&string
);
401 if (*string
!= '=' && *string
!= '<' && *string
!= '>'
407 // search the (in)equation for the actual equation symbol (and for other operators
408 // in case the equation is malformed)
409 while (*string
&& *string
!= '=' && *string
!= '<' && *string
!= '>'
410 && *string
!= '!' && *string
!= '&' && *string
!= '|') {
414 // get the attribute string (and trim whitespace), in case
415 // the string was not quoted
417 skipWhitespaceReverse(&end
, start
);
420 // attribute string is empty (which is not allowed)
424 // At this point, "start" points to the beginning of the string, "end"
425 // points to the last character of the string, and "string" points to the
426 // first character of the equation symbol
428 // test for the right symbol (as this doesn't need any memory)
431 Term
<QueryPolicy
>::fOp
= OP_EQUAL
;
434 Term
<QueryPolicy
>::fOp
= *(string
+ 1) == '='
435 ? OP_GREATER_THAN_OR_EQUAL
: OP_GREATER_THAN
;
438 Term
<QueryPolicy
>::fOp
= *(string
+ 1) == '='
439 ? OP_LESS_THAN_OR_EQUAL
: OP_LESS_THAN
;
442 if (*(string
+ 1) != '=')
444 Term
<QueryPolicy
>::fOp
= OP_UNEQUAL
;
447 // any invalid characters will be rejected
453 // lets change "start" to point to the first character after the symbol
454 if (*(string
+ 1) == '=')
457 skipWhitespace(&string
);
459 // allocate & copy the attribute string
461 fAttribute
= CopyString(start
, end
);
462 if (fAttribute
== NULL
)
466 if (*start
== '"' || *start
== '\'') {
467 // string is quoted (start has to be on the beginning of a string)
468 if (ParseQuotedString(&start
, &end
) < B_OK
)
472 skipWhitespace(&string
);
474 while (*string
&& *string
!= '&' && *string
!= '|' && *string
!= ')')
478 skipWhitespaceReverse(&end
, start
);
481 // at this point, "start" will point to the first character of the value,
482 // "end" will point to its last character, and "start" to the first non-
483 // whitespace character after the value string
485 fString
= CopyString(start
, end
);
489 // patterns are only allowed for these operations (and strings)
490 if (Term
<QueryPolicy
>::fOp
== OP_EQUAL
491 || Term
<QueryPolicy
>::fOp
== OP_UNEQUAL
) {
492 fIsPattern
= isPattern(fString
);
493 if (fIsPattern
&& isValidPattern(fString
) < B_OK
) {
494 // we only want to have valid patterns; setting fString
495 // to NULL will cause InitCheck() to fail
505 template<typename QueryPolicy
>
506 Equation
<QueryPolicy
>::~Equation()
513 template<typename QueryPolicy
>
515 Equation
<QueryPolicy
>::InitCheck()
517 if (fAttribute
== NULL
|| fString
== NULL
518 || Term
<QueryPolicy
>::fOp
== OP_NONE
) {
526 template<typename QueryPolicy
>
528 Equation
<QueryPolicy
>::ParseQuotedString(char** _start
, char** _end
)
530 char* start
= *_start
;
531 char quote
= *start
++;
534 for (; *end
&& *end
!= quote
; end
++) {
548 template<typename QueryPolicy
>
550 Equation
<QueryPolicy
>::CopyString(char* start
, char* end
)
552 // end points to the last character of the string - and the length
553 // also has to include the null-termination
554 int32 length
= end
+ 2 - start
;
555 // just to make sure; since that's the max. attribute name length and
556 // the max. string in an index, it make sense to have it that way
557 if (length
> QueryPolicy::kMaxFileNameLength
|| length
<= 0)
560 char* copy
= (char*)malloc(length
);
564 memcpy(copy
, start
, length
- 1);
565 copy
[length
- 1] = '\0';
571 template<typename QueryPolicy
>
573 Equation
<QueryPolicy
>::ConvertValue(type_code type
)
575 // Has the type already been converted?
579 char* string
= fString
;
582 case B_MIME_STRING_TYPE
:
583 type
= B_STRING_TYPE
;
584 // supposed to fall through
586 strncpy(fValue
.String
, string
, QueryPolicy::kMaxFileNameLength
);
587 fValue
.String
[QueryPolicy::kMaxFileNameLength
- 1] = '\0';
588 fSize
= strlen(fValue
.String
);
591 fValue
.Int32
= strtol(string
, &string
, 0);
592 fSize
= sizeof(int32
);
595 fValue
.Int32
= strtoul(string
, &string
, 0);
596 fSize
= sizeof(uint32
);
599 fValue
.Int64
= strtoll(string
, &string
, 0);
600 fSize
= sizeof(int64
);
603 fValue
.Uint64
= strtoull(string
, &string
, 0);
604 fSize
= sizeof(uint64
);
607 fValue
.Float
= strtod(string
, &string
);
608 fSize
= sizeof(float);
611 fValue
.Double
= strtod(string
, &string
);
612 fSize
= sizeof(double);
615 QUERY_FATAL("query value conversion to 0x%x requested!\n",
617 // should we fail here or just do a safety int32 conversion?
623 // patterns are only allowed for string types
624 if (fType
!= B_STRING_TYPE
&& fIsPattern
)
631 /*! Returns true when the key matches the equation. You have to
632 call ConvertValue() before this one.
634 template<typename QueryPolicy
>
636 Equation
<QueryPolicy
>::CompareTo(const uint8
* value
, size_t size
)
640 // fIsPattern is only true if it's a string type, and fOp OP_EQUAL, or
643 // we have already validated the pattern, so we don't check for failing
644 // here - if something is broken, and matchString() returns an error,
645 // we just don't match
646 compare
= matchString(fValue
.String
, (char*)value
) == MATCH_OK
? 0 : 1;
648 compare
= compareKeys(fType
, value
, size
, Value(), fSize
);
650 switch (Term
<QueryPolicy
>::fOp
) {
657 case OP_LESS_THAN_OR_EQUAL
:
659 case OP_GREATER_THAN
:
661 case OP_GREATER_THAN_OR_EQUAL
:
664 QUERY_FATAL("Unknown/Unsupported operation: %d\n", Term
<QueryPolicy
>::fOp
);
669 template<typename QueryPolicy
>
671 Equation
<QueryPolicy
>::Complement()
673 QUERY_D(if (fOp
<= OP_EQUATION
|| fOp
> OP_LESS_THAN_OR_EQUAL
) {
674 QUERY_FATAL("op out of range!\n");
678 int8 complementOp
[] = {OP_UNEQUAL
, OP_EQUAL
, OP_LESS_THAN_OR_EQUAL
,
679 OP_GREATER_THAN_OR_EQUAL
, OP_LESS_THAN
, OP_GREATER_THAN
};
680 Term
<QueryPolicy
>::fOp
= complementOp
[Term
<QueryPolicy
>::fOp
- OP_EQUAL
];
684 template<typename QueryPolicy
>
686 Equation
<QueryPolicy
>::MatchEmptyString()
688 // There is no matching attribute, we will just bail out if we
689 // already know that our value is not of a string type.
690 // If not, it will be converted to a string - and then be compared with "".
691 // That's why we have to call ConvertValue() here - but it will be
692 // a cheap call for the next time
693 // TODO: Should we do this only for OP_UNEQUAL?
694 if (fType
!= 0 && fType
!= B_STRING_TYPE
)
697 status_t status
= ConvertValue(B_STRING_TYPE
);
699 status
= CompareTo((const uint8
*)"", fSize
) ? MATCH_OK
: NO_MATCH
;
705 /*! Matches the node's attribute value with the equation.
706 Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went
709 template<typename QueryPolicy
>
711 Equation
<QueryPolicy
>::Match(Entry
* entry
, Node
* node
,
712 const char* attributeName
, int32 type
, const uint8
* key
, size_t size
)
714 // get a pointer to the attribute in question
715 union value
<QueryPolicy
> value
;
716 uint8
* buffer
= (uint8
*)&value
;
717 const size_t bufferSize
= sizeof(value
);
719 // first, check if we are matching for a live query and use that value
720 if (attributeName
!= NULL
&& !strcmp(fAttribute
, attributeName
)) {
722 if (type
== B_STRING_TYPE
)
723 return MatchEmptyString();
727 buffer
= const_cast<uint8
*>(key
);
728 } else if (!strcmp(fAttribute
, "name")) {
729 // if not, check for "fake" attributes ("name", "size", "last_modified")
732 buffer
= (uint8
*)QueryPolicy::EntryGetNameNoCopy(entry
, buffer
,
737 type
= B_STRING_TYPE
;
738 size
= strlen((const char*)buffer
);
739 } else if (!strcmp(fAttribute
, "size")) {
740 value
.Int64
= QueryPolicy::NodeGetSize(node
);
742 } else if (!strcmp(fAttribute
, "last_modified")) {
743 value
.Int32
= QueryPolicy::NodeGetLastModifiedTime(node
);
746 // then for attributes
748 if (QueryPolicy::NodeGetAttribute(node
, fAttribute
, buffer
, &size
,
750 return MatchEmptyString();
754 // prepare own value for use, if it is possible to convert it
755 status_t status
= ConvertValue(type
);
757 status
= CompareTo(buffer
, size
) ? MATCH_OK
: NO_MATCH
;
759 QUERY_RETURN_ERROR(status
);
763 template<typename QueryPolicy
>
765 Equation
<QueryPolicy
>::CalculateScore(Index
&index
)
767 // As always, these values could be tuned and refined.
768 // And the code could also need some real world testing :-)
770 // do we have to operate on a "foreign" index?
771 if (Term
<QueryPolicy
>::fOp
== OP_UNEQUAL
772 || QueryPolicy::IndexSetTo(index
, fAttribute
) < B_OK
) {
777 // if we have a pattern, how much does it help our search?
779 fScore
= getFirstPatternSymbol(fString
) << 3;
782 if (Term
<QueryPolicy
>::fOp
== OP_EQUAL
) {
783 // higher than pattern="255 chars+*"
786 // the pattern search is regarded cheaper when you have at
787 // least one character to set your index to
792 // take index size into account
793 fScore
= QueryPolicy::IndexGetWeightedScore(index
, fScore
);
797 template<typename QueryPolicy
>
799 Equation
<QueryPolicy
>::PrepareQuery(Context
* /*context*/, Index
& index
,
800 IndexIterator
** iterator
, bool queryNonIndexed
)
802 status_t status
= QueryPolicy::IndexSetTo(index
, fAttribute
);
804 // if we should query attributes without an index, we can just proceed here
805 if (status
!= B_OK
&& !queryNonIndexed
)
806 return B_ENTRY_NOT_FOUND
;
810 // Special case for OP_UNEQUAL - it will always operate through the whole
811 // index but we need the call to the original index to get the correct type
812 if (status
!= B_OK
|| Term
<QueryPolicy
>::fOp
== OP_UNEQUAL
) {
813 // Try to get an index that holds all files (name)
814 // Also sets the default type for all attributes without index
816 type
= status
< B_OK
? B_STRING_TYPE
: QueryPolicy::IndexGetType(index
);
818 if (QueryPolicy::IndexSetTo(index
, "name") != B_OK
)
819 return B_ENTRY_NOT_FOUND
;
824 type
= QueryPolicy::IndexGetType(index
);
827 if (ConvertValue(type
) < B_OK
)
830 *iterator
= QueryPolicy::IndexCreateIterator(index
);
831 if (*iterator
== NULL
)
834 if ((Term
<QueryPolicy
>::fOp
== OP_EQUAL
835 || Term
<QueryPolicy
>::fOp
== OP_GREATER_THAN
836 || Term
<QueryPolicy
>::fOp
== OP_GREATER_THAN_OR_EQUAL
|| fIsPattern
)
838 // set iterator to the exact position
840 int32 keySize
= QueryPolicy::IndexGetKeySize(index
);
842 // At this point, fIsPattern is only true if it's a string type, and fOp
843 // is either OP_EQUAL or OP_UNEQUAL
845 // let's see if we can use the beginning of the key for positioning
846 // the iterator and adjust the key size; if not, just leave the
847 // iterator at the start and return success
848 keySize
= getFirstPatternSymbol(fString
);
854 // B_STRING_TYPE doesn't have a fixed length, so it was set
855 // to 0 before - we compute the correct value here
856 if (fType
== B_STRING_TYPE
) {
857 keySize
= strlen(fValue
.String
);
859 // The empty string is a special case - we normally don't check
860 // for the trailing null byte, in the case for the empty string
861 // we do it explicitly, because there can't be keys in the
862 // B+tree with a length of zero
866 QUERY_RETURN_ERROR(B_ENTRY_NOT_FOUND
);
869 status
= QueryPolicy::IndexIteratorFind(*iterator
, Value(), keySize
);
870 if (Term
<QueryPolicy
>::fOp
== OP_EQUAL
&& !fIsPattern
)
872 else if (status
== B_ENTRY_NOT_FOUND
873 && (fIsPattern
|| Term
<QueryPolicy
>::fOp
== OP_GREATER_THAN
874 || Term
<QueryPolicy
>::fOp
== OP_GREATER_THAN_OR_EQUAL
))
877 QUERY_RETURN_ERROR(status
);
884 template<typename QueryPolicy
>
886 Equation
<QueryPolicy
>::GetNextMatching(Context
* context
,
887 IndexIterator
* iterator
, struct dirent
* dirent
, size_t bufferSize
)
890 union value
<QueryPolicy
> indexValue
;
894 status_t status
= QueryPolicy::IndexIteratorGetNextEntry(iterator
,
895 &indexValue
, &keyLength
, (size_t)sizeof(indexValue
), &entry
);
899 // only compare against the index entry when this is the correct
900 // index for the equation
901 if (fHasIndex
&& !CompareTo((uint8
*)&indexValue
, keyLength
)) {
902 // They aren't equal? Let the operation decide what to do. Since
903 // we always start at the beginning of the index (or the correct
904 // position), only some needs to be stopped if the entry doesn't
906 if (Term
<QueryPolicy
>::fOp
== OP_LESS_THAN
907 || Term
<QueryPolicy
>::fOp
== OP_LESS_THAN_OR_EQUAL
908 || (Term
<QueryPolicy
>::fOp
== OP_EQUAL
&& !fIsPattern
))
909 return B_ENTRY_NOT_FOUND
;
914 // TODO: check user permissions here - but which one?!
915 // we could filter out all those where we don't have
916 // read access... (we should check for every parent
917 // directory if the X_OK is allowed)
918 // Although it's quite expensive to open all parents,
919 // it's likely that the application that runs the
920 // query will do something similar (and we don't have
921 // to do it for root, either).
923 // go up in the tree until a &&-operator is found, and check if the
924 // node matches with the rest of the expression - we don't have to
925 // check ||-operators for that
926 Term
<QueryPolicy
>* term
= this;
930 status
= Match(entry
, QueryPolicy::EntryGetNode(entry
));
932 while (term
!= NULL
&& status
== MATCH_OK
) {
933 Operator
<QueryPolicy
>* parent
934 = (Operator
<QueryPolicy
>*)term
->Parent();
938 if (parent
->Op() == OP_AND
) {
939 // choose the other child of the parent
940 Term
<QueryPolicy
>* other
= parent
->Right();
942 other
= parent
->Left();
945 QUERY_FATAL("&&-operator has only one child... "
946 "(parent = %p)\n", parent
);
949 status
= other
->Match(entry
, QueryPolicy::EntryGetNode(entry
));
951 QUERY_REPORT_ERROR(status
);
955 term
= (Term
<QueryPolicy
>*)parent
;
958 if (status
== MATCH_OK
) {
959 ssize_t nameLength
= QueryPolicy::EntryGetName(entry
,
961 (const char*)dirent
+ bufferSize
- dirent
->d_name
);
963 QUERY_RETURN_ERROR(nameLength
);
965 dirent
->d_dev
= QueryPolicy::ContextGetVolumeID(context
);
966 dirent
->d_ino
= QueryPolicy::EntryGetNodeID(entry
);
967 dirent
->d_pdev
= dirent
->d_dev
;
968 dirent
->d_pino
= QueryPolicy::EntryGetParentID(entry
);
969 dirent
->d_reclen
= sizeof(struct dirent
) + strlen(dirent
->d_name
);
972 if (status
== MATCH_OK
)
975 QUERY_RETURN_ERROR(B_ERROR
);
979 template<typename QueryPolicy
>
981 Equation
<QueryPolicy
>::NeedsEntry()
983 return strcmp(fAttribute
, "name") == 0;
990 template<typename QueryPolicy
>
991 Operator
<QueryPolicy
>::Operator(Term
<QueryPolicy
>* left
, int8 op
,
992 Term
<QueryPolicy
>* right
)
994 Term
<QueryPolicy
>(op
),
999 left
->SetParent(this);
1001 right
->SetParent(this);
1005 template<typename QueryPolicy
>
1006 Operator
<QueryPolicy
>::~Operator()
1013 template<typename QueryPolicy
>
1015 Operator
<QueryPolicy
>::Match(Entry
* entry
, Node
* node
, const char* attribute
,
1016 int32 type
, const uint8
* key
, size_t size
)
1018 if (Term
<QueryPolicy
>::fOp
== OP_AND
) {
1019 status_t status
= fLeft
->Match(entry
, node
, attribute
, type
, key
,
1021 if (status
!= MATCH_OK
)
1024 return fRight
->Match(entry
, node
, attribute
, type
, key
, size
);
1026 // choose the term with the better score for OP_OR
1027 Term
<QueryPolicy
>* first
;
1028 Term
<QueryPolicy
>* second
;
1029 if (fRight
->Score() > fLeft
->Score()) {
1037 status_t status
= first
->Match(entry
, node
, attribute
, type
, key
,
1039 if (status
!= NO_MATCH
)
1042 return second
->Match(entry
, node
, attribute
, type
, key
, size
);
1047 template<typename QueryPolicy
>
1049 Operator
<QueryPolicy
>::Complement()
1051 if (Term
<QueryPolicy
>::fOp
== OP_AND
)
1052 Term
<QueryPolicy
>::fOp
= OP_OR
;
1054 Term
<QueryPolicy
>::fOp
= OP_AND
;
1056 fLeft
->Complement();
1057 fRight
->Complement();
1061 template<typename QueryPolicy
>
1063 Operator
<QueryPolicy
>::CalculateScore(Index
&index
)
1065 fLeft
->CalculateScore(index
);
1066 fRight
->CalculateScore(index
);
1070 template<typename QueryPolicy
>
1072 Operator
<QueryPolicy
>::Score() const
1074 if (Term
<QueryPolicy
>::fOp
== OP_AND
) {
1075 // return the one with the better score
1076 if (fRight
->Score() > fLeft
->Score())
1077 return fRight
->Score();
1079 return fLeft
->Score();
1082 // for OP_OR, be honest, and return the one with the worse score
1083 if (fRight
->Score() < fLeft
->Score())
1084 return fRight
->Score();
1086 return fLeft
->Score();
1090 template<typename QueryPolicy
>
1092 Operator
<QueryPolicy
>::InitCheck()
1094 if ((Term
<QueryPolicy
>::fOp
!= OP_AND
&& Term
<QueryPolicy
>::fOp
!= OP_OR
)
1095 || fLeft
== NULL
|| fLeft
->InitCheck() < B_OK
1096 || fRight
== NULL
|| fRight
->InitCheck() < B_OK
)
1103 template<typename QueryPolicy
>
1105 Operator
<QueryPolicy
>::NeedsEntry()
1107 return ((fLeft
&& fLeft
->NeedsEntry()) || (fRight
&& fRight
->NeedsEntry()));
1115 template<typename QueryPolicy
>
1117 Operator
<QueryPolicy
>::PrintToStream()
1119 QUERY_D(__out("( "));
1121 fLeft
->PrintToStream();
1124 switch (Term
<QueryPolicy
>::fOp
) {
1125 case OP_OR
: op
= "OR"; break;
1126 case OP_AND
: op
= "AND"; break;
1127 default: op
= "?"; break;
1129 QUERY_D(__out(" %s ", op
));
1132 fRight
->PrintToStream();
1134 QUERY_D(__out(" )"));
1138 template<typename QueryPolicy
>
1140 Equation
<QueryPolicy
>::PrintToStream()
1142 const char* symbol
= "???";
1143 switch (Term
<QueryPolicy
>::fOp
) {
1144 case OP_EQUAL
: symbol
= "=="; break;
1145 case OP_UNEQUAL
: symbol
= "!="; break;
1146 case OP_GREATER_THAN
: symbol
= ">"; break;
1147 case OP_GREATER_THAN_OR_EQUAL
: symbol
= ">="; break;
1148 case OP_LESS_THAN
: symbol
= "<"; break;
1149 case OP_LESS_THAN_OR_EQUAL
: symbol
= "<="; break;
1151 QUERY_D(__out("[\"%s\" %s \"%s\"]", fAttribute
, symbol
, fString
));
1154 #endif // DEBUG_QUERY
1159 template<typename QueryPolicy
>
1160 Expression
<QueryPolicy
>::Expression(char* expr
)
1165 fTerm
= ParseOr(&expr
);
1166 if (fTerm
!= NULL
&& fTerm
->InitCheck() < B_OK
) {
1167 QUERY_FATAL("Corrupt tree in expression!\n");
1171 QUERY_D(if (fTerm
!= NULL
) {
1172 fTerm
->PrintToStream();
1173 QUERY_D(__out("\n"));
1175 PRINT(("Unexpected end of string: \"%s\"!\n", expr
));
1181 template<typename QueryPolicy
>
1182 Expression
<QueryPolicy
>::~Expression()
1188 template<typename QueryPolicy
>
1190 Expression
<QueryPolicy
>::ParseEquation(char** expr
)
1192 skipWhitespace(expr
);
1195 if (**expr
== '!') {
1196 skipWhitespace(expr
, 1);
1203 if (**expr
== ')') {
1204 // shouldn't be handled here
1206 } else if (**expr
== '(') {
1207 skipWhitespace(expr
, 1);
1209 Term
<QueryPolicy
>* term
= ParseOr(expr
);
1211 skipWhitespace(expr
);
1213 if (**expr
!= ')') {
1218 // If the term is negated, we just complement the tree, to get
1219 // rid of the not, a.k.a. DeMorgan's Law.
1223 skipWhitespace(expr
, 1);
1228 Equation
<QueryPolicy
>* equation
1229 = new(std::nothrow
) Equation
<QueryPolicy
>(expr
);
1230 if (equation
== NULL
|| equation
->InitCheck() < B_OK
) {
1238 template<typename QueryPolicy
>
1240 Expression
<QueryPolicy
>::ParseAnd(char** expr
)
1242 Term
<QueryPolicy
>* left
= ParseEquation(expr
);
1246 while (IsOperator(expr
, '&')) {
1247 Term
<QueryPolicy
>* right
= ParseAnd(expr
);
1248 Term
<QueryPolicy
>* newParent
= NULL
;
1251 || (newParent
= new(std::nothrow
) Operator
<QueryPolicy
>(left
,
1252 OP_AND
, right
)) == NULL
) {
1265 template<typename QueryPolicy
>
1267 Expression
<QueryPolicy
>::ParseOr(char** expr
)
1269 Term
<QueryPolicy
>* left
= ParseAnd(expr
);
1273 while (IsOperator(expr
, '|')) {
1274 Term
<QueryPolicy
>* right
= ParseAnd(expr
);
1275 Term
<QueryPolicy
>* newParent
= NULL
;
1278 || (newParent
= new(std::nothrow
) Operator
<QueryPolicy
>(left
, OP_OR
,
1292 template<typename QueryPolicy
>
1294 Expression
<QueryPolicy
>::IsOperator(char** expr
, char op
)
1296 char* string
= *expr
;
1298 if (*string
== op
&& *(string
+ 1) == op
) {
1306 template<typename QueryPolicy
>
1308 Expression
<QueryPolicy
>::InitCheck()
1320 template<typename QueryPolicy
>
1321 Query
<QueryPolicy
>::Query(Context
* context
, Expression
<QueryPolicy
>* expression
,
1322 uint32 flags
, port_id port
, uint32 token
)
1325 fExpression(expression
),
1334 // If the expression has a valid root pointer, the whole tree has
1335 // already passed the sanity check, so that we don't have to check
1337 if (context
== NULL
|| expression
== NULL
|| expression
->Root() == NULL
)
1340 // create index on the stack and delete it afterwards
1341 fExpression
->Root()->CalculateScore(fIndex
);
1342 QueryPolicy::IndexUnset(fIndex
);
1344 fNeedsEntry
= fExpression
->Root()->NeedsEntry();
1350 template<typename QueryPolicy
>
1351 Query
<QueryPolicy
>::~Query()
1357 template<typename QueryPolicy
>
1359 Query
<QueryPolicy
>::Create(Context
* context
, const char* queryString
,
1360 uint32 flags
, port_id port
, uint32 token
, Query
<QueryPolicy
>*& _query
)
1362 Expression
<QueryPolicy
>* expression
1363 = new(std::nothrow
) Expression
<QueryPolicy
>((char*)queryString
);
1364 if (expression
== NULL
)
1365 QUERY_RETURN_ERROR(B_NO_MEMORY
);
1367 if (expression
->InitCheck() != B_OK
) {
1368 QUERY_INFORM("Could not parse query \"%s\", stopped at: \"%s\"\n",
1369 queryString
, expression
->Position());
1372 QUERY_RETURN_ERROR(B_BAD_VALUE
);
1375 Query
<QueryPolicy
>* query
= new(std::nothrow
) Query
<QueryPolicy
>(context
,
1376 expression
, flags
, port
, token
);
1377 if (query
== NULL
) {
1379 QUERY_RETURN_ERROR(B_NO_MEMORY
);
1387 template<typename QueryPolicy
>
1389 Query
<QueryPolicy
>::Rewind()
1391 // free previous stuff
1395 QueryPolicy::IndexIteratorDelete(fIterator
);
1399 // put the whole expression on the stack
1401 Stack
<Term
<QueryPolicy
>*> stack
;
1402 stack
.Push(fExpression
->Root());
1404 Term
<QueryPolicy
>* term
;
1405 while (stack
.Pop(&term
)) {
1406 if (term
->Op() < OP_EQUATION
) {
1407 Operator
<QueryPolicy
>* op
= (Operator
<QueryPolicy
>*)term
;
1409 if (op
->Op() == OP_OR
) {
1410 stack
.Push(op
->Left());
1411 stack
.Push(op
->Right());
1413 // For OP_AND, we can use the scoring system to decide which
1415 if (op
->Right()->Score() > op
->Left()->Score())
1416 stack
.Push(op
->Right());
1418 stack
.Push(op
->Left());
1420 } else if (term
->Op() == OP_EQUATION
1421 || fStack
.Push((Equation
<QueryPolicy
>*)term
) != B_OK
)
1422 QUERY_FATAL("Unknown term on stack or stack error\n");
1429 template<typename QueryPolicy
>
1431 Query
<QueryPolicy
>::GetNextEntry(struct dirent
* dirent
, size_t size
)
1433 if (fIterator
!= NULL
)
1434 QueryPolicy::IndexIteratorResume(fIterator
);
1436 status_t error
= _GetNextEntry(dirent
, size
);
1438 if (fIterator
!= NULL
)
1439 QueryPolicy::IndexIteratorSuspend(fIterator
);
1445 template<typename QueryPolicy
>
1447 Query
<QueryPolicy
>::LiveUpdate(Entry
* entry
, Node
* node
, const char* attribute
,
1448 int32 type
, const uint8
* oldKey
, size_t oldLength
, const uint8
* newKey
,
1451 if (fPort
< 0 || fExpression
== NULL
|| attribute
== NULL
)
1454 // TODO: check if the attribute is part of the query at all...
1456 // If no entry has been supplied, but the we need one for the evaluation
1457 // (i.e. the "name" attribute is used), we invoke ourselves for all entries
1458 // referring to the given node.
1459 if (entry
== NULL
&& fNeedsEntry
) {
1460 entry
= QueryPolicy::NodeGetFirstReferrer(node
);
1462 LiveUpdate(entry
, node
, attribute
, type
, oldKey
, oldLength
, newKey
,
1464 entry
= QueryPolicy::NodeGetNextReferrer(node
, entry
);
1469 status_t oldStatus
= fExpression
->Root()->Match(entry
, node
, attribute
,
1470 type
, oldKey
, oldLength
);
1471 status_t newStatus
= fExpression
->Root()->Match(entry
, node
, attribute
,
1472 type
, newKey
, newLength
);
1474 bool entryCreated
= false;
1475 bool stillInQuery
= false;
1477 if (oldStatus
!= MATCH_OK
) {
1478 if (newStatus
!= MATCH_OK
) {
1479 // nothing has changed
1482 entryCreated
= true;
1483 } else if (newStatus
!= MATCH_OK
) {
1484 // entry got removed
1485 entryCreated
= false;
1486 } else if ((fFlags
& B_ATTR_CHANGE_NOTIFICATION
) != 0) {
1487 // The entry stays in the query
1488 stillInQuery
= true;
1492 // notify query listeners
1493 status_t (*notify
)(port_id
, int32
, dev_t
, ino_t
, const char*, ino_t
);
1496 notify
= notify_query_attr_changed
;
1497 else if (entryCreated
)
1498 notify
= notify_query_entry_created
;
1500 notify
= notify_query_entry_removed
;
1502 if (entry
!= NULL
) {
1503 _SendEntryNotification(entry
, notify
);
1505 entry
= QueryPolicy::NodeGetFirstReferrer(node
);
1507 _SendEntryNotification(entry
, notify
);
1508 entry
= QueryPolicy::NodeGetNextReferrer(node
, entry
);
1514 template<typename QueryPolicy
>
1516 Query
<QueryPolicy
>::LiveUpdateRenameMove(Entry
* entry
, Node
* node
,
1517 ino_t oldDirectoryID
, const char* oldName
, size_t oldLength
,
1518 ino_t newDirectoryID
, const char* newName
, size_t newLength
)
1520 if (fPort
< 0 || fExpression
== NULL
)
1523 // TODO: check if the attribute is part of the query at all...
1525 status_t oldStatus
= fExpression
->Root()->Match(entry
, node
, "name",
1526 B_STRING_TYPE
, (const uint8
*)oldName
, oldLength
);
1527 status_t newStatus
= fExpression
->Root()->Match(entry
, node
, "name",
1528 B_STRING_TYPE
, (const uint8
*)newName
, newLength
);
1530 if (oldStatus
!= MATCH_OK
|| oldStatus
!= newStatus
)
1533 // The entry stays in the query, notify query listeners about the rename
1536 // We send a notification for the given entry, if any, or otherwise for
1537 // all entries referring to the node;
1538 if (entry
!= NULL
) {
1539 _SendEntryNotification(entry
, notify_query_entry_removed
);
1540 _SendEntryNotification(entry
, notify_query_entry_created
);
1542 entry
= QueryPolicy::NodeGetFirstReferrer(node
);
1544 _SendEntryNotification(entry
, notify_query_entry_removed
);
1545 _SendEntryNotification(entry
, notify_query_entry_created
);
1546 entry
= QueryPolicy::NodeGetNextReferrer(node
, entry
);
1552 template<typename QueryPolicy
>
1554 Query
<QueryPolicy
>::_GetNextEntry(struct dirent
* dirent
, size_t size
)
1556 // If we don't have an equation to use yet/anymore, get a new one
1559 if (fIterator
== NULL
) {
1560 if (!fStack
.Pop(&fCurrent
)
1561 || fCurrent
== NULL
)
1562 return B_ENTRY_NOT_FOUND
;
1564 status_t status
= fCurrent
->PrepareQuery(fContext
, fIndex
,
1565 &fIterator
, fFlags
& B_QUERY_NON_INDEXED
);
1566 if (status
== B_ENTRY_NOT_FOUND
) {
1567 // try next equation
1574 if (fCurrent
== NULL
)
1575 QUERY_RETURN_ERROR(B_ERROR
);
1577 status_t status
= fCurrent
->GetNextMatching(fContext
, fIterator
, dirent
,
1579 if (status
!= B_OK
) {
1580 QueryPolicy::IndexIteratorDelete(fIterator
);
1584 // only return if we have another entry
1591 template<typename QueryPolicy
>
1593 Query
<QueryPolicy
>::_SendEntryNotification(Entry
* entry
,
1594 status_t (*notify
)(port_id
, int32
, dev_t
, ino_t
, const char*, ino_t
))
1596 char nameBuffer
[QueryPolicy::kMaxFileNameLength
];
1597 const char* name
= QueryPolicy::EntryGetNameNoCopy(entry
, nameBuffer
,
1598 sizeof(nameBuffer
));
1600 notify(fPort
, fToken
, QueryPolicy::ContextGetVolumeID(fContext
),
1601 QueryPolicy::EntryGetParentID(entry
), name
,
1602 QueryPolicy::EntryGetNodeID(entry
));
1607 } // namespace QueryParser
1610 #endif // _FILE_SYSTEMS_QUERY_PARSER_H