* Copyright 2001-2014, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef _FILE_SYSTEMS_QUERY_PARSER_H
#define _FILE_SYSTEMS_QUERY_PARSER_H
#ifdef FS_SHELL
# include <new>
# include "fssh_api_wrapper.h"
# include "fssh_auto_deleter.h"
#else
# include <dirent.h>
# include <stdlib.h>
# include <string.h>
# include <algorithm>
# include <new>
# include <fs_interface.h>
# include <fs_query.h>
# include <NodeMonitor.h>
# include <TypeConstants.h>
# include <util/SinglyLinkedList.h>
# include <util/Stack.h>
# include <query_private.h>
#endif
#include <file_systems/QueryParserUtils.h>
#ifndef QUERY_RETURN_ERROR
# define QUERY_RETURN_ERROR(error) return (error)
#endif
#ifndef QUERY_REPORT_ERROR
# define QUERY_REPORT_ERROR(error) do {} while (false)
#endif
#ifndef QUERY_FATAL
# define QUERY_FATAL(message...) panic(message)
#endif
#ifndef QUERY_INFORM
# define QUERY_INFORM(message...) dprintf(message)
#endif
#ifndef QUERY_D
# define QUERY_D(block)
#endif
namespace QueryParser {
template<typename QueryPolicy> class Equation;
template<typename QueryPolicy> class Expression;
template<typename QueryPolicy> class Term;
template<typename QueryPolicy> class Query;
enum ops {
OP_NONE,
OP_AND,
OP_OR,
OP_EQUATION,
OP_EQUAL,
OP_UNEQUAL,
OP_GREATER_THAN,
OP_LESS_THAN,
OP_GREATER_THAN_OR_EQUAL,
OP_LESS_THAN_OR_EQUAL,
};
template<typename QueryPolicy>
union value {
int64 Int64;
uint64 Uint64;
int32 Int32;
uint32 Uint32;
float Float;
double Double;
char String[QueryPolicy::kMaxFileNameLength];
};
template<typename QueryPolicy>
class Query {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Query(Context* context,
Expression<QueryPolicy>* expression,
uint32 flags, port_id port, uint32 token);
~Query();
static status_t Create(Context* context, const char* queryString,
uint32 flags, port_id port, uint32 token,
Query<QueryPolicy>*& _query);
status_t Rewind();
inline status_t GetNextEntry(struct dirent* dirent, size_t size);
void LiveUpdate(Entry* entry, Node* node,
const char* attribute, int32 type,
const uint8* oldKey, size_t oldLength,
const uint8* newKey, size_t newLength);
void LiveUpdateRenameMove(Entry* entry, Node* node,
ino_t oldDirectoryID, const char* oldName,
size_t oldLength, ino_t newDirectoryID,
const char* newName, size_t newLength);
Expression<QueryPolicy>* GetExpression() const
{ return fExpression; }
uint32 Flags() const
{ return fFlags; }
private:
status_t _GetNextEntry(struct dirent* dirent, size_t size);
void _EvaluateLiveUpdate(Entry* entry, Node* node,
const char* attribute, int32 type,
const uint8* oldKey, size_t oldLength,
const uint8* newKey, size_t newLength,
int32& opcode);
void _SendEntryNotification(Entry* entry,
int32 opcode, const char* attribute, int32 cause);
private:
Context* fContext;
Expression<QueryPolicy>* fExpression;
Equation<QueryPolicy>* fCurrent;
IndexIterator* fIterator;
Index fIndex;
Stack<Equation<QueryPolicy>*> fStack;
uint32 fFlags;
port_id fPort;
int32 fToken;
bool fNeedsEntry;
};
*/
template<typename QueryPolicy>
class Term {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Term(int8 op) : fOp(op), fParent(NULL) {}
virtual ~Term() {}
int8 Op() const { return fOp; }
void SetParent(Term<QueryPolicy>* parent)
{ fParent = parent; }
Term<QueryPolicy>* Parent() const { return fParent; }
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0) = 0;
virtual void Complement() = 0;
virtual void CalculateScore(Index& index) = 0;
virtual int32 Score() const = 0;
virtual status_t InitCheck() = 0;
virtual bool NeedsEntry() = 0;
#ifdef DEBUG_QUERY
virtual void PrintToStream() = 0;
#endif
protected:
int8 fOp;
Term<QueryPolicy>* fParent;
};
Although an Equation object is quite independent from the volume on which
the query is run, there are some dependencies that are produced while
querying:
The type/size of the value, the score, and if it has an index or not.
So you could run more than one query on the same volume, but it might return
wrong values when it runs concurrently on another volume.
That's not an issue right now, because we run single-threaded and don't use
queries more than once.
*/
template<typename QueryPolicy>
class Equation : public Term<QueryPolicy> {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Equation(const char** expression);
virtual ~Equation();
virtual status_t InitCheck();
status_t ParseQuotedString(const char** _start, const char** _end);
char* CopyString(const char* start, const char* end);
inline bool _IsEquationChar(char c) const;
inline bool _IsOperatorChar(char c) const;
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0);
virtual void Complement();
status_t PrepareQuery(Context* context, Index& index,
IndexIterator** iterator, bool queryNonIndexed);
status_t GetNextMatching(Context* context,
IndexIterator* iterator, struct dirent* dirent,
size_t bufferSize);
virtual void CalculateScore(Index &index);
virtual int32 Score() const { return fScore; }
virtual bool NeedsEntry();
#ifdef DEBUG_QUERY
virtual void PrintToStream();
#endif
private:
Equation(const Equation& other);
Equation& operator=(const Equation& other);
status_t ConvertValue(type_code type, uint32 size);
bool CompareTo(const uint8* value, size_t size);
uint8* Value() const { return (uint8*)&fValue; }
char* fAttribute;
char* fString;
union value<QueryPolicy> fValue;
type_code fType;
uint32 fSize;
bool fIsPattern;
int32 fScore;
bool fHasIndex;
};
that combine two equations, namely "or", and "and".
*/
template<typename QueryPolicy>
class Operator : public Term<QueryPolicy> {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::Context Context;
public:
Operator(Term<QueryPolicy>* left, int8 op,
Term<QueryPolicy>* right);
virtual ~Operator();
Term<QueryPolicy>* Left() const { return fLeft; }
Term<QueryPolicy>* Right() const { return fRight; }
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0);
virtual void Complement();
virtual void CalculateScore(Index& index);
virtual int32 Score() const;
virtual status_t InitCheck();
virtual bool NeedsEntry();
#ifdef DEBUG_QUERY
virtual void PrintToStream();
#endif
private:
Operator(const Operator& other);
Operator& operator=(const Operator& other);
Term<QueryPolicy>* fLeft;
Term<QueryPolicy>* fRight;
};
template<typename QueryPolicy>
class Expression {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::Context Context;
public:
Expression();
~Expression();
status_t Init(const char* expr, const char** position);
Term<QueryPolicy>* Root() const { return fTerm; }
protected:
bool IsOperator(const char** expr, char op);
private:
Expression(const Expression& other);
Expression& operator=(const Expression& other);
Term<QueryPolicy>* fTerm;
};
template<typename QueryPolicy>
Equation<QueryPolicy>::Equation(const char** expr)
:
Term<QueryPolicy>(OP_EQUATION),
fAttribute(NULL),
fString(NULL),
fType(0),
fSize(0),
fIsPattern(false),
fScore(INT32_MAX)
{
const char* string = *expr;
const char* start = string;
const char* end = NULL;
if (*start == '"' || *start == '\'') {
if (ParseQuotedString(&start, &end) < B_OK)
return;
string = end + 2;
skipWhitespace(&string);
if (!_IsEquationChar(string[0])) {
*expr = string;
return;
}
} else {
while (string[0] != 0 && !_IsOperatorChar(string[0])
&& !_IsEquationChar(string[0])) {
if (string[0] == '\\' && string[1] != 0)
string++;
string++;
}
end = string - 1;
skipWhitespaceReverse(&end, start);
}
if (start > end)
return;
switch (*string) {
case '=':
Term<QueryPolicy>::fOp = OP_EQUAL;
break;
case '>':
Term<QueryPolicy>::fOp = *(string + 1) == '='
? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN;
break;
case '<':
Term<QueryPolicy>::fOp = *(string + 1) == '='
? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN;
break;
case '!':
if (*(string + 1) != '=')
return;
Term<QueryPolicy>::fOp = OP_UNEQUAL;
break;
default:
*expr = string;
return;
}
if (*(string + 1) == '=')
string++;
string++;
skipWhitespace(&string);
fAttribute = CopyString(start, end);
if (fAttribute == NULL)
return;
start = string;
if (*start == '"' || *start == '\'') {
if (ParseQuotedString(&start, &end) < B_OK)
return;
string = end + 2;
skipWhitespace(&string);
} else {
while (string[0] && !_IsOperatorChar(string[0]) && string[0] != ')')
string++;
end = string - 1;
skipWhitespaceReverse(&end, start);
}
fString = CopyString(start, end);
if (fString == NULL)
return;
if (Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_UNEQUAL) {
fIsPattern = isPattern(fString);
if (fIsPattern && isValidPattern(fString) < B_OK) {
free(fString);
fString = NULL;
}
}
*expr = string;
}
template<typename QueryPolicy>
Equation<QueryPolicy>::~Equation()
{
free(fAttribute);
free(fString);
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::InitCheck()
{
if (fAttribute == NULL || fString == NULL
|| Term<QueryPolicy>::fOp == OP_NONE) {
return B_BAD_VALUE;
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::ParseQuotedString(const char** _start, const char** _end)
{
const char* start = *_start;
const char quote = *start++;
const char* end = start;
for (; *end && *end != quote; end++) {
if (*end == '\\')
end++;
}
if (*end == '\0')
return B_BAD_VALUE;
*_start = start;
*_end = end - 1;
return B_OK;
}
template<typename QueryPolicy>
char*
Equation<QueryPolicy>::CopyString(const char* start, const char* end)
{
int32 length = end + 2 - start;
if (length > QueryPolicy::kMaxFileNameLength || length <= 0)
return NULL;
char* copy = (char*)malloc(length);
if (copy == NULL)
return NULL;
for (int32 i = 0; i < length; i++) {
char c = start++[0];
if (c == '\\' && i < length) {
length--;
i--;
continue;
}
copy[i] = c;
}
copy[length - 1] = '\0';
return copy;
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::_IsEquationChar(char c) const
{
return c == '=' || c == '<' || c == '>' || c == '!';
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::_IsOperatorChar(char c) const
{
return c == '&' || c == '|';
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::ConvertValue(type_code type, uint32 size)
{
if (type == B_MIME_STRING_TYPE)
type = B_STRING_TYPE;
else if (type == B_TIME_TYPE)
type = (size == 4) ? B_INT32_TYPE : B_INT64_TYPE;
if (type == fType)
return B_OK;
char* string = fString;
switch (type) {
case B_STRING_TYPE:
strncpy(fValue.String, string, QueryPolicy::kMaxFileNameLength);
fValue.String[QueryPolicy::kMaxFileNameLength - 1] = '\0';
fSize = strlen(fValue.String);
break;
case B_INT32_TYPE:
fValue.Int32 = strtol(string, &string, 0);
fSize = sizeof(int32);
break;
case B_UINT32_TYPE:
fValue.Int32 = strtoul(string, &string, 0);
fSize = sizeof(uint32);
break;
case B_INT64_TYPE:
fValue.Int64 = strtoll(string, &string, 0);
fSize = sizeof(int64);
break;
case B_UINT64_TYPE:
fValue.Uint64 = strtoull(string, &string, 0);
fSize = sizeof(uint64);
break;
case B_FLOAT_TYPE:
fValue.Float = strtod(string, &string);
fSize = sizeof(float);
break;
case B_DOUBLE_TYPE:
fValue.Double = strtod(string, &string);
fSize = sizeof(double);
break;
default:
QUERY_INFORM("query attribute '%s': unsupported value conversion to 0x%x requested!\n",
fAttribute, (int)type);
return B_BAD_TYPE;
}
fType = type;
if (fType != B_STRING_TYPE && fIsPattern)
fIsPattern = false;
return B_OK;
}
call ConvertValue() before this one.
*/
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::CompareTo(const uint8* value, size_t size)
{
int32 compare;
if (fIsPattern) {
compare = matchString(fValue.String, (const char*)value) == MATCH_OK ? 0 : 1;
} else
compare = compareKeys(fType, value, size, Value(), fSize);
switch (Term<QueryPolicy>::fOp) {
case OP_EQUAL:
return compare == 0;
case OP_UNEQUAL:
return compare != 0;
case OP_LESS_THAN:
return compare < 0;
case OP_LESS_THAN_OR_EQUAL:
return compare <= 0;
case OP_GREATER_THAN:
return compare > 0;
case OP_GREATER_THAN_OR_EQUAL:
return compare >= 0;
}
QUERY_FATAL("Unknown/Unsupported operation: %d\n", Term<QueryPolicy>::fOp);
return false;
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::Complement()
{
QUERY_D(if (Term<QueryPolicy>::fOp <= OP_EQUATION || Term<QueryPolicy>::fOp > OP_LESS_THAN_OR_EQUAL) {
QUERY_FATAL("op out of range!\n");
return;
});
int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL,
OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN};
Term<QueryPolicy>::fOp = complementOp[Term<QueryPolicy>::fOp - OP_EQUAL];
}
Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went
wrong.
*/
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::Match(Entry* entry, Node* node,
const char* attributeName, int32 type, const uint8* key, size_t size)
{
NodeHolder nodeHolder;
union value<QueryPolicy> value;
uint8* buffer = (uint8*)&value;
const size_t bufferSize = sizeof(value);
if (attributeName != NULL && !strcmp(fAttribute, attributeName)) {
if (key == NULL)
return NO_MATCH;
buffer = const_cast<uint8*>(key);
} else if (!strcmp(fAttribute, "name")) {
if (entry == NULL)
return B_ERROR;
buffer = (uint8*)QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (buffer == NULL)
return B_ERROR;
type = B_STRING_TYPE;
size = strlen((const char*)buffer);
} else if (!strcmp(fAttribute, "size")) {
value.Int64 = QueryPolicy::NodeGetSize(node);
type = B_INT64_TYPE;
} else if (!strcmp(fAttribute, "last_modified")) {
value.Int64 = QueryPolicy::NodeGetLastModifiedTime(node);
type = B_INT64_TYPE;
} else {
size = bufferSize;
if (QueryPolicy::NodeGetAttribute(nodeHolder, node,
fAttribute, buffer, &size, &type) != B_OK) {
return NO_MATCH;
}
}
status_t status = ConvertValue(type, size);
if (status == B_OK)
status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH;
QUERY_RETURN_ERROR(status);
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::CalculateScore(Index &index)
{
if (QueryPolicy::IndexSetTo(index, fAttribute) != B_OK) {
fScore = INT32_MAX;
return;
}
if (ConvertValue(QueryPolicy::IndexGetType(index),
QueryPolicy::IndexGetKeySize(index)) != B_OK) {
fScore = INT32_MAX;
return;
}
fScore = QueryPolicy::IndexGetSize(index);
if (Term<QueryPolicy>::fOp == OP_UNEQUAL) {
return;
}
if (fIsPattern) {
const int32 firstSymbolIndex = getFirstPatternSymbol(fString);
const int32 divisor = (firstSymbolIndex > 3) ? 4 : (firstSymbolIndex + 1);
fScore /= divisor;
} else {
if (Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL) {
fScore /= (fSize > 8) ? 8 : fSize;
} else {
fScore /= 2;
}
}
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::PrepareQuery(Context* , Index& index,
IndexIterator** iterator, bool queryNonIndexed)
{
status_t status = QueryPolicy::IndexSetTo(index, fAttribute);
if (status != B_OK && !queryNonIndexed)
return B_ENTRY_NOT_FOUND;
type_code type;
int32 keySize;
if (status != B_OK || Term<QueryPolicy>::fOp == OP_UNEQUAL) {
if (status == B_OK) {
type = QueryPolicy::IndexGetType(index);
keySize = QueryPolicy::IndexGetKeySize(index);
} else {
type = B_STRING_TYPE;
keySize = 0;
}
if (QueryPolicy::IndexSetTo(index, "name") != B_OK)
return B_ENTRY_NOT_FOUND;
fHasIndex = false;
} else {
fHasIndex = true;
type = QueryPolicy::IndexGetType(index);
keySize = QueryPolicy::IndexGetKeySize(index);
}
if (ConvertValue(type, keySize) < B_OK)
return B_BAD_VALUE;
*iterator = QueryPolicy::IndexCreateIterator(index);
if (*iterator == NULL)
return B_NO_MEMORY;
if ((Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL || fIsPattern)
&& fHasIndex) {
if (fIsPattern) {
keySize = getFirstPatternSymbol(fString);
if (keySize <= 0)
return B_OK;
}
if (keySize == 0) {
if (fType == B_STRING_TYPE) {
keySize = strlen(fValue.String);
if (keySize == 0)
keySize = 1;
} else
QUERY_RETURN_ERROR(B_ENTRY_NOT_FOUND);
}
status = QueryPolicy::IndexIteratorFind(*iterator, Value(), keySize);
if (Term<QueryPolicy>::fOp == OP_EQUAL && !fIsPattern)
return status;
else if (status == B_ENTRY_NOT_FOUND
&& (fIsPattern || Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL))
return B_OK;
QUERY_RETURN_ERROR(status);
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::GetNextMatching(Context* context,
IndexIterator* iterator, struct dirent* dirent, size_t bufferSize)
{
while (true) {
NodeHolder nodeHolder;
union value<QueryPolicy> indexValue;
size_t keyLength;
size_t duplicate = 0;
status_t status = QueryPolicy::IndexIteratorFetchNextEntry(iterator,
&indexValue, &keyLength, (size_t)sizeof(indexValue), &duplicate);
if (status != B_OK)
return status;
if (fHasIndex && duplicate < 2 && !CompareTo((uint8*)&indexValue, keyLength)) {
if (Term<QueryPolicy>::fOp == OP_LESS_THAN
|| Term<QueryPolicy>::fOp == OP_LESS_THAN_OR_EQUAL
|| (Term<QueryPolicy>::fOp == OP_EQUAL && !fIsPattern))
return B_ENTRY_NOT_FOUND;
if (duplicate > 0)
QueryPolicy::IndexIteratorSkipDuplicates(iterator);
continue;
}
Entry* entry = NULL;
status = QueryPolicy::IndexIteratorGetEntry(context, iterator,
nodeHolder, &entry);
if (status != B_OK) {
continue;
}
Term<QueryPolicy>* term = this;
status = MATCH_OK;
if (!fHasIndex)
status = Match(entry, QueryPolicy::EntryGetNode(entry));
while (term != NULL && status == MATCH_OK) {
Operator<QueryPolicy>* parent
= (Operator<QueryPolicy>*)term->Parent();
if (parent == NULL)
break;
if (parent->Op() == OP_AND) {
Term<QueryPolicy>* other = parent->Right();
if (other == term)
other = parent->Left();
if (other == NULL) {
QUERY_FATAL("&&-operator has only one child... "
"(parent = %p)\n", parent);
break;
}
status = other->Match(entry, QueryPolicy::EntryGetNode(entry));
if (status < 0) {
QUERY_REPORT_ERROR(status);
status = NO_MATCH;
}
}
term = (Term<QueryPolicy>*)parent;
}
if (status == MATCH_OK) {
ssize_t nameLength = QueryPolicy::EntryGetName(entry,
dirent->d_name,
(const char*)dirent + bufferSize - dirent->d_name);
if (nameLength < 0) {
nameLength = 0;
}
dirent->d_dev = QueryPolicy::ContextGetVolumeID(context);
dirent->d_ino = QueryPolicy::EntryGetNodeID(entry);
dirent->d_pdev = dirent->d_dev;
dirent->d_pino = QueryPolicy::EntryGetParentID(entry);
dirent->d_reclen = offsetof(struct dirent, d_name) + nameLength;
}
if (status == MATCH_OK)
return B_OK;
}
QUERY_RETURN_ERROR(B_ERROR);
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::NeedsEntry()
{
return strcmp(fAttribute, "name") == 0;
}
template<typename QueryPolicy>
Operator<QueryPolicy>::Operator(Term<QueryPolicy>* left, int8 op,
Term<QueryPolicy>* right)
:
Term<QueryPolicy>(op),
fLeft(left),
fRight(right)
{
if (left)
left->SetParent(this);
if (right)
right->SetParent(this);
}
template<typename QueryPolicy>
Operator<QueryPolicy>::~Operator()
{
delete fLeft;
delete fRight;
}
template<typename QueryPolicy>
status_t
Operator<QueryPolicy>::Match(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* key, size_t size)
{
if (Term<QueryPolicy>::fOp == OP_AND) {
status_t status = fLeft->Match(entry, node, attribute, type, key,
size);
if (status != MATCH_OK)
return status;
return fRight->Match(entry, node, attribute, type, key, size);
} else {
Term<QueryPolicy>* first;
Term<QueryPolicy>* second;
if (fRight->Score() < fLeft->Score()) {
first = fLeft;
second = fRight;
} else {
first = fRight;
second = fLeft;
}
status_t status = first->Match(entry, node, attribute, type, key,
size);
if (status != NO_MATCH)
return status;
return second->Match(entry, node, attribute, type, key, size);
}
}
template<typename QueryPolicy>
void
Operator<QueryPolicy>::Complement()
{
if (Term<QueryPolicy>::fOp == OP_AND)
Term<QueryPolicy>::fOp = OP_OR;
else
Term<QueryPolicy>::fOp = OP_AND;
fLeft->Complement();
fRight->Complement();
}
template<typename QueryPolicy>
void
Operator<QueryPolicy>::CalculateScore(Index &index)
{
fLeft->CalculateScore(index);
fRight->CalculateScore(index);
}
template<typename QueryPolicy>
int32
Operator<QueryPolicy>::Score() const
{
if (Term<QueryPolicy>::fOp == OP_AND) {
if (fRight->Score() < fLeft->Score())
return fRight->Score();
return fLeft->Score();
}
if (fRight->Score() > fLeft->Score())
return fRight->Score();
return fLeft->Score();
}
template<typename QueryPolicy>
status_t
Operator<QueryPolicy>::InitCheck()
{
if ((Term<QueryPolicy>::fOp != OP_AND && Term<QueryPolicy>::fOp != OP_OR)
|| fLeft == NULL || fLeft->InitCheck() < B_OK
|| fRight == NULL || fRight->InitCheck() < B_OK)
return B_ERROR;
return B_OK;
}
template<typename QueryPolicy>
bool
Operator<QueryPolicy>::NeedsEntry()
{
return ((fLeft && fLeft->NeedsEntry()) || (fRight && fRight->NeedsEntry()));
}
#ifdef DEBUG_QUERY
template<typename QueryPolicy>
void
Operator<QueryPolicy>::PrintToStream()
{
QUERY_D(__out("( "));
if (fLeft != NULL)
fLeft->PrintToStream();
const char* op;
switch (Term<QueryPolicy>::fOp) {
case OP_OR: op = "OR"; break;
case OP_AND: op = "AND"; break;
default: op = "?"; break;
}
QUERY_D(__out(" %s ", op));
if (fRight != NULL)
fRight->PrintToStream();
QUERY_D(__out(" )"));
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::PrintToStream()
{
const char* symbol = "???";
switch (Term<QueryPolicy>::fOp) {
case OP_EQUAL: symbol = "=="; break;
case OP_UNEQUAL: symbol = "!="; break;
case OP_GREATER_THAN: symbol = ">"; break;
case OP_GREATER_THAN_OR_EQUAL: symbol = ">="; break;
case OP_LESS_THAN: symbol = "<"; break;
case OP_LESS_THAN_OR_EQUAL: symbol = "<="; break;
}
QUERY_D(__out("[\"%s\" %s \"%s\"]", fAttribute, symbol, fString));
}
#endif
template<typename QueryPolicy>
Expression<QueryPolicy>::Expression()
:
fTerm(NULL)
{
}
template<typename QueryPolicy>
status_t
Expression<QueryPolicy>::Init(const char* expr, const char** position)
{
if (expr == NULL)
return B_BAD_VALUE;
if (fTerm != NULL)
return EALREADY;
status_t status = B_OK;
int32 equations = 0;
const int32 kMaxEquations = 32;
struct ExpressionNode {
Term<QueryPolicy>* term = NULL;
bool negated = false;
ops op = OP_NONE;
};
Stack<Stack<ExpressionNode>*> exprsTree;
Stack<ExpressionNode>* currentExpr = NULL;
ExpressionNode* current = NULL;
while (expr != NULL) {
skipWhitespace(&expr);
if (currentExpr == NULL) {
currentExpr = new(std::nothrow) Stack<ExpressionNode>;
if (currentExpr == NULL) {
status = B_NO_MEMORY;
break;
}
}
const char c = *expr;
bool complete = false;
if (c == ')' || c == '\0') {
if (currentExpr->IsEmpty())
break;
complete = true;
}
if (current == NULL && !complete) {
currentExpr->Push(ExpressionNode());
current = currentExpr->Array() + (currentExpr->CountItems() - 1);
}
if (c == '(') {
exprsTree.Push(currentExpr);
currentExpr = NULL;
current = NULL;
expr++;
} else if (c == '!') {
skipWhitespace(&expr, 1);
if (*expr != '(')
break;
current->negated = true;
} else if (c == '|' || c == '&') {
if (current->term == NULL)
break;
ops op = OP_NONE;
if (IsOperator(&expr, '|'))
op = OP_OR;
else if (IsOperator(&expr, '&'))
op = OP_AND;
else
break;
current->op = op;
current = NULL;
} else if (!complete) {
if (current->term != NULL)
break;
if ((equations + 1) > kMaxEquations) {
status = E2BIG;
break;
}
Equation<QueryPolicy>* equation
= new(std::nothrow) Equation<QueryPolicy>(&expr);
if (equation == NULL) {
status = B_NO_MEMORY;
break;
}
if (equation == NULL || equation->InitCheck() != B_OK) {
status = equation->InitCheck();
delete equation;
break;
}
current->term = equation;
equations++;
}
if (!complete)
continue;
if (currentExpr->CountItems() == 1) {
if (current == NULL)
break;
}
for (int32 i = 0; i < currentExpr->CountItems(); i++) {
current = currentExpr->Array() + i;
if (current->negated) {
current->term->Complement();
current->negated = false;
}
}
int32 nodes = currentExpr->CountItems();
for (ops op = OP_AND; op <= OP_OR; op = (ops)(op + 1)) {
for (int32 i = 0; i < (currentExpr->CountItems() - 1); ) {
ExpressionNode* left = currentExpr->Array() + i;
if (left->op != op) {
i++;
continue;
}
ExpressionNode* right = NULL;
for (int32 j = i + 1; j < currentExpr->CountItems(); j++) {
current = currentExpr->Array() + j;
if (current->term == NULL)
continue;
right = current;
break;
}
if (right == NULL)
break;
Term<QueryPolicy>* newTerm = new(std::nothrow) Operator<QueryPolicy>(
left->term, left->op, right->term);
if (newTerm == NULL) {
status = B_NO_MEMORY;
break;
}
left->term = newTerm;
left->op = right->op;
right->op = OP_NONE;
right->term = NULL;
nodes--;
}
}
if (nodes != 1)
break;
current = currentExpr->Array();
Term<QueryPolicy>* term = current->term;
delete currentExpr;
currentExpr = NULL;
if (exprsTree.Pop(¤tExpr)) {
current = currentExpr->Array() + (currentExpr->CountItems() - 1);
if (current->term != NULL)
break;
current->term = term;
} else {
if (c != '\0')
break;
fTerm = term;
break;
}
expr++;
}
if (position != NULL)
*position = expr;
do {
if (currentExpr == NULL)
continue;
ExpressionNode item;
while (currentExpr->Pop(&item))
delete item.term;
delete currentExpr;
} while (exprsTree.Pop(¤tExpr));
if (fTerm == NULL && status == B_OK)
return B_BAD_VALUE;
QUERY_D(if (fTerm != NULL) {
fTerm->PrintToStream();
QUERY_D(__out("\n"));
if (*expr != '\0')
PRINT(("Unexpected end of string: \"%s\"!\n", expr));
});
return status;
}
template<typename QueryPolicy>
Expression<QueryPolicy>::~Expression()
{
delete fTerm;
}
template<typename QueryPolicy>
bool
Expression<QueryPolicy>::IsOperator(const char** expr, char op)
{
const char* string = *expr;
if (*string == op && *(string + 1) == op) {
*expr += 2;
return true;
}
return false;
}
template<typename QueryPolicy>
Query<QueryPolicy>::Query(Context* context, Expression<QueryPolicy>* expression,
uint32 flags, port_id port, uint32 token)
:
fContext(context),
fExpression(expression),
fCurrent(NULL),
fIterator(NULL),
fIndex(context),
fFlags(flags),
fPort(port),
fToken(token),
fNeedsEntry(false)
{
if (context == NULL || expression == NULL || expression->Root() == NULL)
return;
fExpression->Root()->CalculateScore(fIndex);
QueryPolicy::IndexUnset(fIndex);
fNeedsEntry = fExpression->Root()->NeedsEntry();
Rewind();
}
template<typename QueryPolicy>
Query<QueryPolicy>::~Query()
{
delete fExpression;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::Create(Context* context, const char* queryString,
uint32 flags, port_id port, uint32 token, Query<QueryPolicy>*& _query)
{
Expression<QueryPolicy>* expression
= new(std::nothrow) Expression<QueryPolicy>;
if (expression == NULL)
QUERY_RETURN_ERROR(B_NO_MEMORY);
const char* position = NULL;
status_t status = expression->Init(queryString, &position);
if (status != B_OK) {
QUERY_INFORM("Could not parse query \"%s\", stopped at: \"%s\"\n",
queryString, position);
delete expression;
QUERY_RETURN_ERROR(status);
}
Query<QueryPolicy>* query = new(std::nothrow) Query<QueryPolicy>(context,
expression, flags, port, token);
if (query == NULL) {
delete expression;
QUERY_RETURN_ERROR(B_NO_MEMORY);
}
_query = query;
return B_OK;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::Rewind()
{
fStack.MakeEmpty();
QueryPolicy::IndexIteratorDelete(fIterator);
fIterator = NULL;
fCurrent = NULL;
Stack<Term<QueryPolicy>*> stack;
stack.Push(fExpression->Root());
Term<QueryPolicy>* term;
while (stack.Pop(&term)) {
if (term->Op() < OP_EQUATION) {
Operator<QueryPolicy>* op = (Operator<QueryPolicy>*)term;
if (op->Op() == OP_OR) {
stack.Push(op->Left());
stack.Push(op->Right());
} else {
if (op->Right()->Score() < op->Left()->Score())
stack.Push(op->Right());
else
stack.Push(op->Left());
}
} else if (term->Op() == OP_EQUATION
|| fStack.Push((Equation<QueryPolicy>*)term) != B_OK)
QUERY_FATAL("Unknown term on stack or stack error\n");
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::GetNextEntry(struct dirent* dirent, size_t size)
{
if (fIterator != NULL)
QueryPolicy::IndexIteratorResume(fIterator);
status_t error = _GetNextEntry(dirent, size);
if (fIterator != NULL)
QueryPolicy::IndexIteratorSuspend(fIterator);
return error;
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::LiveUpdate(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* oldKey, size_t oldLength, const uint8* newKey,
size_t newLength)
{
if (fPort < 0 || fExpression == NULL || attribute == NULL)
return;
if (entry == NULL && fNeedsEntry) {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry != NULL) {
LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey,
newLength);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
}
return;
}
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, attribute, type, oldKey, oldLength,
newKey, newLength, opcode);
if (opcode <= 0)
return;
int32 cause = B_ATTR_CHANGED;
if (opcode == B_ATTR_CHANGED) {
if (oldKey == NULL && newKey != NULL)
cause = B_ATTR_CREATED;
else if (oldKey != NULL && newKey == NULL)
cause = B_ATTR_REMOVED;
}
if (entry != NULL) {
_SendEntryNotification(entry, opcode, attribute, cause);
} else {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry != NULL) {
_SendEntryNotification(entry, opcode, attribute, cause);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
}
}
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::LiveUpdateRenameMove(Entry* entry, Node* node,
ino_t oldDirectoryID, const char* oldName, size_t oldLength,
ino_t newDirectoryID, const char* newName, size_t newLength)
{
if (fPort < 0 || fExpression == NULL)
return;
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, "name", B_STRING_TYPE,
(const uint8*)oldName, oldLength, (const uint8*)newName, newLength,
opcode);
if (opcode < 0)
return;
if (opcode == 0 || opcode == B_ATTR_CHANGED) {
if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
notify_query_entry_moved(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, newDirectoryID, newName,
QueryPolicy::EntryGetNodeID(entry));
} else {
notify_query_entry_removed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, QueryPolicy::EntryGetNodeID(entry));
notify_query_entry_created(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
newDirectoryID, newName, QueryPolicy::EntryGetNodeID(entry));
}
return;
}
_SendEntryNotification(entry, opcode, NULL, B_ATTR_CHANGED);
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::_EvaluateLiveUpdate(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* oldKey, size_t oldLength, const uint8* newKey,
size_t newLength, int32& opcode)
{
if (fPort < 0 || fExpression == NULL)
return;
status_t oldStatus = fExpression->Root()->Match(entry, node, attribute,
type, oldKey, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, node, attribute,
type, newKey, newLength);
if (oldStatus != MATCH_OK) {
if (newStatus != MATCH_OK) {
return;
}
opcode = B_ENTRY_CREATED;
} else if (newStatus != MATCH_OK) {
opcode = B_ENTRY_REMOVED;
} else if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
opcode = B_ATTR_CHANGED;
} else {
opcode = 0;
}
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::_GetNextEntry(struct dirent* dirent, size_t size)
{
while (true) {
if (fIterator == NULL) {
if (!fStack.Pop(&fCurrent)
|| fCurrent == NULL)
return B_ENTRY_NOT_FOUND;
status_t status = fCurrent->PrepareQuery(fContext, fIndex,
&fIterator, fFlags & B_QUERY_NON_INDEXED);
if (status == B_ENTRY_NOT_FOUND) {
continue;
}
if (status != B_OK)
return status;
}
if (fCurrent == NULL)
QUERY_RETURN_ERROR(B_ERROR);
status_t status = fCurrent->GetNextMatching(fContext, fIterator, dirent,
size);
if (status != B_OK) {
QueryPolicy::IndexIteratorDelete(fIterator);
fIterator = NULL;
fCurrent = NULL;
} else {
return B_OK;
}
}
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::_SendEntryNotification(Entry* entry, int32 opcode,
const char* attribute, int32 cause)
{
switch (opcode) {
case B_ENTRY_CREATED:
case B_ENTRY_REMOVED:
{
NodeHolder nodeHolder;
const char* name = QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (name != NULL) {
((opcode == B_ENTRY_CREATED) ?
notify_query_entry_created : notify_query_entry_removed)
(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), name,
QueryPolicy::EntryGetNodeID(entry));
}
break;
}
case B_ATTR_CHANGED:
notify_query_attribute_changed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), QueryPolicy::EntryGetNodeID(entry),
attribute, cause);
break;
}
}
}
#endif