xref: /haiku/src/kits/storage/Query.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 //----------------------------------------------------------------------
2 //  This software is part of the Haiku distribution and is covered
3 //  by the MIT license.
4 //---------------------------------------------------------------------
5 /*!
6 	\file Query.cpp
7 	BQuery implementation.
8 */
9 
10 #include <fcntl.h>
11 #include <new>
12 #include <parsedate.h>
13 #include <time.h>
14 
15 #include <Entry.h>
16 #include <fs_query.h>
17 #include <Query.h>
18 #include <Volume.h>
19 
20 #include <MessengerPrivate.h>
21 #include <syscalls.h>
22 
23 #include "QueryPredicate.h"
24 #include "storage_support.h"
25 
26 using namespace std;
27 using namespace BPrivate::Storage;
28 
29 // BQuery
30 
31 // constructor
32 /*!	\brief Creates an uninitialized BQuery.
33 */
34 BQuery::BQuery()
35 	  : BEntryList(),
36 		fStack(NULL),
37 		fPredicate(NULL),
38 		fDevice((dev_t)B_ERROR),
39 		fLive(false),
40 		fPort(B_ERROR),
41 		fToken(0),
42 		fQueryFd(-1)
43 {
44 }
45 
46 // destructor
47 /*!	\brief Frees all resources associated with the object.
48 */
49 BQuery::~BQuery()
50 {
51 	Clear();
52 }
53 
54 // Clear
55 /*!	\brief Resets the object to a uninitialized state.
56 	\return \c B_OK
57 */
58 status_t
59 BQuery::Clear()
60 {
61 	// close the currently open query
62 	status_t error = B_OK;
63 	if (fQueryFd >= 0) {
64 		error = _kern_close(fQueryFd);
65 		fQueryFd = -1;
66 	}
67 	// delete the predicate stack and the predicate
68 	delete fStack;
69 	fStack = NULL;
70 	delete[] fPredicate;
71 	fPredicate = NULL;
72 	// reset the other parameters
73 	fDevice = (dev_t)B_ERROR;
74 	fLive = false;
75 	fPort = B_ERROR;
76 	fToken = 0;
77 	return error;
78 }
79 
80 // PushAttr
81 /*!	\brief Pushes an attribute name onto the BQuery's predicate stack.
82 	\param attrName the attribute name
83 	\return
84 	- \c B_OK: Everything went fine.
85 	- \c B_NO_MEMORY: Not enough memory.
86 	- \c B_NOT_ALLOWED: PushAttribute() was called after Fetch().
87 	\note In BeOS R5 this method returns \c void. That is checking the return
88 		  value will render your code source and binary incompatible!
89 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
90 		  but it doesn't affect the active query and the newly created
91 		  predicate can not even be used for the next query, since in order
92 		  to be able to reuse the BQuery object for another query, Clear() has
93 		  to be called and Clear() also deletes the predicate.
94 */
95 status_t
96 BQuery::PushAttr(const char *attrName)
97 {
98 	return _PushNode(new(nothrow) AttributeNode(attrName), true);
99 }
100 
101 // PushOp
102 /*!	\brief Pushes an operator onto the BQuery's predicate stack.
103 	\param op the code representing the operator
104 	\return
105 	- \c B_OK: Everything went fine.
106 	- \c B_NO_MEMORY: Not enough memory.
107 	- \c B_NOT_ALLOWED: PushOp() was called after Fetch().
108 	\note In BeOS R5 this method returns \c void. That is checking the return
109 		  value will render your code source and binary incompatible!
110 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
111 		  but it doesn't affect the active query and the newly created
112 		  predicate can not even be used for the next query, since in order
113 		  to be able to reuse the BQuery object for another query, Clear() has
114 		  to be called and Clear() also deletes the predicate.
115 */
116 status_t
117 BQuery::PushOp(query_op op)
118 {
119 	status_t error = B_OK;
120 	switch (op) {
121 		case B_EQ:
122 		case B_GT:
123 		case B_GE:
124 		case B_LT:
125 		case B_LE:
126 		case B_NE:
127 		case B_CONTAINS:
128 		case B_BEGINS_WITH:
129 		case B_ENDS_WITH:
130 		case B_AND:
131 		case B_OR:
132 			error = _PushNode(new(nothrow) BinaryOpNode(op), true);
133 			break;
134 		case B_NOT:
135 			error = _PushNode(new(nothrow) UnaryOpNode(op), true);
136 			break;
137 		default:
138 			error = _PushNode(new(nothrow) SpecialOpNode(op), true);
139 			break;
140 	}
141 	return error;
142 }
143 
144 // PushUInt32
145 /*!	\brief Pushes a uint32 value onto the BQuery's predicate stack.
146 	\param value the value
147 	\return
148 	- \c B_OK: Everything went fine.
149 	- \c B_NO_MEMORY: Not enough memory.
150 	- \c B_NOT_ALLOWED: PushUInt32() was called after Fetch().
151 	\note In BeOS R5 this method returns \c void. That is checking the return
152 		  value will render your code source and binary incompatible!
153 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
154 		  but it doesn't affect the active query and the newly created
155 		  predicate can not even be used for the next query, since in order
156 		  to be able to reuse the BQuery object for another query, Clear() has
157 		  to be called and Clear() also deletes the predicate.
158 */
159 status_t
160 BQuery::PushUInt32(uint32 value)
161 {
162 	return _PushNode(new(nothrow) UInt32ValueNode(value), true);
163 }
164 
165 // PushInt32
166 /*!	\brief Pushes an int32 value onto the BQuery's predicate stack.
167 	\param value the value
168 	\return
169 	- \c B_OK: Everything went fine.
170 	- \c B_NO_MEMORY: Not enough memory.
171 	- \c B_NOT_ALLOWED: PushInt32() was called after Fetch().
172 	\note In BeOS R5 this method returns \c void. That is checking the return
173 		  value will render your code source and binary incompatible!
174 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
175 		  but it doesn't affect the active query and the newly created
176 		  predicate can not even be used for the next query, since in order
177 		  to be able to reuse the BQuery object for another query, Clear() has
178 		  to be called and Clear() also deletes the predicate.
179 */
180 status_t
181 BQuery::PushInt32(int32 value)
182 {
183 	return _PushNode(new(nothrow) Int32ValueNode(value), true);
184 }
185 
186 // PushUInt64
187 /*!	\brief Pushes a uint64 value onto the BQuery's predicate stack.
188 	\param value the value
189 	\return
190 	- \c B_OK: Everything went fine.
191 	- \c B_NO_MEMORY: Not enough memory.
192 	- \c B_NOT_ALLOWED: PushUInt64() was called after Fetch().
193 	\note In BeOS R5 this method returns \c void. That is checking the return
194 		  value will render your code source and binary incompatible!
195 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
196 		  but it doesn't affect the active query and the newly created
197 		  predicate can not even be used for the next query, since in order
198 		  to be able to reuse the BQuery object for another query, Clear() has
199 		  to be called and Clear() also deletes the predicate.
200 */
201 status_t
202 BQuery::PushUInt64(uint64 value)
203 {
204 	return _PushNode(new(nothrow) UInt64ValueNode(value), true);
205 }
206 
207 // PushInt64
208 /*!	\brief Pushes an int64 value onto the BQuery's predicate stack.
209 	\param value the value
210 	\return
211 	- \c B_OK: Everything went fine.
212 	- \c B_NO_MEMORY: Not enough memory.
213 	- \c B_NOT_ALLOWED: PushInt64() was called after Fetch().
214 	\note In BeOS R5 this method returns \c void. That is checking the return
215 		  value will render your code source and binary incompatible!
216 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
217 		  but it doesn't affect the active query and the newly created
218 		  predicate can not even be used for the next query, since in order
219 		  to be able to reuse the BQuery object for another query, Clear() has
220 		  to be called and Clear() also deletes the predicate.
221 */
222 status_t
223 BQuery::PushInt64(int64 value)
224 {
225 	return _PushNode(new(nothrow) Int64ValueNode(value), true);
226 }
227 
228 // PushFloat
229 /*!	\brief Pushes a float value onto the BQuery's predicate stack.
230 	\param value the value
231 	\return
232 	- \c B_OK: Everything went fine.
233 	- \c B_NO_MEMORY: Not enough memory.
234 	- \c B_NOT_ALLOWED: PushFloat() was called after Fetch().
235 	\note In BeOS R5 this method returns \c void. That is checking the return
236 		  value will render your code source and binary incompatible!
237 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
238 		  but it doesn't affect the active query and the newly created
239 		  predicate can not even be used for the next query, since in order
240 		  to be able to reuse the BQuery object for another query, Clear() has
241 		  to be called and Clear() also deletes the predicate.
242 */
243 status_t
244 BQuery::PushFloat(float value)
245 {
246 	return _PushNode(new(nothrow) FloatValueNode(value), true);
247 }
248 
249 // PushDouble
250 /*!	\brief Pushes a double value onto the BQuery's predicate stack.
251 	\param value the value
252 	\return
253 	- \c B_OK: Everything went fine.
254 	- \c B_NO_MEMORY: Not enough memory.
255 	- \c B_NOT_ALLOWED: PushDouble() was called after Fetch().
256 	\note In BeOS R5 this method returns \c void. That is checking the return
257 		  value will render your code source and binary incompatible!
258 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
259 		  but it doesn't affect the active query and the newly created
260 		  predicate can not even be used for the next query, since in order
261 		  to be able to reuse the BQuery object for another query, Clear() has
262 		  to be called and Clear() also deletes the predicate.
263 */
264 status_t
265 BQuery::PushDouble(double value)
266 {
267 	return _PushNode(new(nothrow) DoubleValueNode(value), true);
268 }
269 
270 // PushString
271 /*!	\brief Pushes a string value onto the BQuery's predicate stack.
272 	\param value the value
273 	\param caseInsensitive \c true, if the case of the string should be
274 		   ignored, \c false otherwise
275 	\return
276 	- \c B_OK: Everything went fine.
277 	- \c B_NO_MEMORY: Not enough memory.
278 	- \c B_NOT_ALLOWED: PushString() was called after Fetch().
279 	\note In BeOS R5 this method returns \c void. That is checking the return
280 		  value will render your code source and binary incompatible!
281 		  Calling PushXYZ() after a Fetch() does change the predicate on R5,
282 		  but it doesn't affect the active query and the newly created
283 		  predicate can not even be used for the next query, since in order
284 		  to be able to reuse the BQuery object for another query, Clear() has
285 		  to be called and Clear() also deletes the predicate.
286 */
287 status_t
288 BQuery::PushString(const char *value, bool caseInsensitive)
289 {
290 	return _PushNode(new(nothrow) StringNode(value, caseInsensitive), true);
291 }
292 
293 // PushDate
294 /*!	\brief Pushes a date value onto the BQuery's predicate stack.
295 	The supplied date can be any string understood by the POSIX function
296 	parsedate().
297 	\param date the date string
298 	\return
299 	- \c B_OK: Everything went fine.
300 	- \c B_ERROR: Error parsing the string.
301 	- \c B_NOT_ALLOWED: PushDate() was called after Fetch().
302 	\note Calling PushXYZ() after a Fetch() does change the predicate on R5,
303 		  but it doesn't affect the active query and the newly created
304 		  predicate can not even be used for the next query, since in order
305 		  to be able to reuse the BQuery object for another query, Clear() has
306 		  to be called and Clear() also deletes the predicate.
307 */
308 status_t
309 BQuery::PushDate(const char *date)
310 {
311 	status_t error = (date ? B_OK : B_BAD_VALUE);
312 	if (error == B_OK) {
313 		time_t t;
314 		time(&t);
315 		t = parsedate(date, t);
316 		if (t < 0)
317 			error = B_BAD_VALUE;
318 	}
319 	if (error == B_OK)
320 		error =  _PushNode(new(nothrow) DateNode(date), true);
321 	return error;
322 }
323 
324 // SetVolume
325 /*!	\brief Sets the BQuery's volume.
326 	A query is restricted to one volume. This method sets this volume. It
327 	fails, if called after Fetch(). To reuse a BQuery object it has to be
328 	reset via Clear().
329 	\param volume the volume
330 	\return
331 	- \c B_OK: Everything went fine.
332 	- \c B_NOT_ALLOWED: SetVolume() was called after Fetch().
333 */
334 status_t
335 BQuery::SetVolume(const BVolume *volume)
336 {
337 	status_t error = (volume ? B_OK : B_BAD_VALUE);
338 	if (error == B_OK && _HasFetched())
339 		error = B_NOT_ALLOWED;
340 	if (error == B_OK) {
341 		if (volume->InitCheck() == B_OK)
342 			fDevice = volume->Device();
343 		else
344 			fDevice = (dev_t)B_ERROR;
345 	}
346 	return error;
347 }
348 
349 // SetPredicate
350 /*!	\brief Sets the BQuery's predicate.
351 	A predicate can be set either using this method or constructing one on
352 	the predicate stack. The two methods can not be mixed. The letter one
353 	has precedence over this one.
354 	The method fails, if called after Fetch(). To reuse a BQuery object it has
355 	to be reset via Clear().
356 	\param predicate the predicate string
357 	\return
358 	- \c B_OK: Everything went fine.
359 	- \c B_NOT_ALLOWED: SetPredicate() was called after Fetch().
360 	- \c B_NO_MEMORY: Insufficient memory to store the predicate.
361 */
362 status_t
363 BQuery::SetPredicate(const char *expression)
364 {
365 	status_t error = (expression ? B_OK : B_BAD_VALUE);
366 	if (error == B_OK && _HasFetched())
367 		error = B_NOT_ALLOWED;
368 	if (error == B_OK)
369 		error = _SetPredicate(expression);
370 	return error;
371 }
372 
373 // SetTarget
374 /*!	\brief Sets the BQuery's target and makes the query live.
375 	The query update messages are sent to the specified target. They might
376 	roll in immediately after calling Fetch().
377 	This methods fails, if called after Fetch(). To reuse a BQuery object it
378 	has to be reset via Clear().
379 	\return
380 	- \c B_OK: Everything went fine.
381 	- \c B_BAD_VALUE: \a messenger was not properly initialized.
382 	- \c B_NOT_ALLOWED: SetTarget() was called after Fetch().
383 */
384 status_t
385 BQuery::SetTarget(BMessenger messenger)
386 {
387 	status_t error = (messenger.IsValid() ? B_OK : B_BAD_VALUE);
388 	if (error == B_OK && _HasFetched())
389 		error = B_NOT_ALLOWED;
390 	if (error == B_OK) {
391 		BMessenger::Private messengerPrivate(messenger);
392 		fPort = messengerPrivate.Port();
393 		fToken = (messengerPrivate.IsPreferredTarget()
394 			? -1 : messengerPrivate.Token());
395 		fLive = true;
396 	}
397 	return error;
398 }
399 
400 // IsLive
401 /*!	\brief Returns whether the query associated with this object is live.
402 	\return \c true, if the query is live, \c false otherwise
403 */
404 bool
405 BQuery::IsLive() const
406 {
407 	return fLive;
408 }
409 
410 // GetPredicate
411 /*!	\brief Returns the BQuery's predicate.
412 	Regardless of whether the predicate has been constructed using the
413 	predicate stack or set via SetPredicate(), this method returns a
414 	string representation.
415 	\param buffer a pointer to a buffer into which the predicate shall be
416 		   written
417 	\param length the size of the provided buffer
418 	\return
419 	- \c B_OK: Everything went fine.
420 	- \c B_NO_INIT: The predicate isn't set.
421 	- \c B_BAD_VALUE: \a buffer is \c NULL or too short.
422 	\note This method causes the predicate stack to be evaluated and cleared.
423 		  You can't interleave Push*() and GetPredicate() calls.
424 */
425 status_t
426 BQuery::GetPredicate(char *buffer, size_t length)
427 {
428 	status_t error = (buffer ? B_OK : B_BAD_VALUE);
429 	if (error == B_OK)
430 		_EvaluateStack();
431 	if (error == B_OK && !fPredicate)
432 		error = B_NO_INIT;
433 	if (error == B_OK && length <= strlen(fPredicate))
434 		error = B_BAD_VALUE;
435 	if (error == B_OK)
436 		strcpy(buffer, fPredicate);
437 	return error;
438 }
439 
440 // GetPredicate
441 /*!	\brief Returns the BQuery's predicate.
442 	Regardless of whether the predicate has been constructed using the
443 	predicate stack or set via SetPredicate(), this method returns a
444 	string representation.
445 	\param predicate a pointer to a BString which shall be set to the
446 		   predicate string
447 	\return
448 	- \c B_OK: Everything went fine.
449 	- \c B_NO_INIT: The predicate isn't set.
450 	- \c B_BAD_VALUE: \c NULL \a predicate.
451 	\note This method causes the predicate stack to be evaluated and cleared.
452 		  You can't interleave Push*() and GetPredicate() calls.
453 */
454 status_t
455 BQuery::GetPredicate(BString *predicate)
456 {
457 	status_t error = (predicate ? B_OK : B_BAD_VALUE);
458 	if (error == B_OK)
459 		_EvaluateStack();
460 	if (error == B_OK && !fPredicate)
461 		error = B_NO_INIT;
462 	if (error == B_OK)
463 		predicate->SetTo(fPredicate);
464 	return error;
465 }
466 
467 // PredicateLength
468 /*!	\brief Returns the length of the BQuery's predicate string.
469 	Regardless of whether the predicate has been constructed using the
470 	predicate stack or set via SetPredicate(), this method returns the length
471 	of its string representation (counting the terminating null).
472 	\return
473 	- the length of the predicate string (counting the terminating null) or
474 	- 0, if an error occured
475 	\note This method causes the predicate stack to be evaluated and cleared.
476 		  You can't interleave Push*() and PredicateLength() calls.
477 */
478 size_t
479 BQuery::PredicateLength()
480 {
481 	status_t error = _EvaluateStack();
482 	if (error == B_OK && !fPredicate)
483 		error = B_NO_INIT;
484 	size_t size = 0;
485 	if (error == B_OK)
486 		size = strlen(fPredicate) + 1;
487 	return size;
488 }
489 
490 // TargetDevice
491 /*!	\brief Returns the device ID identifying the BQuery's volume.
492 	\return the device ID of the BQuery's volume or \c B_NO_INIT, if the
493 			volume isn't set.
494 
495 */
496 dev_t
497 BQuery::TargetDevice() const
498 {
499 	return fDevice;
500 }
501 
502 // Fetch
503 /*!	\brief Tells the BQuery to start fetching entries satisfying the predicate.
504 	After Fetch() has been called GetNextEntry(), GetNextRef() and
505 	GetNextDirents() can be used to retrieve the enties. Live query updates
506 	may be sent immediately after this method has been called.
507 	Fetch() fails, if it has already been called. To reuse a BQuery object it
508 	has to be reset via Clear().
509 	\return
510 	- \c B_OK: Everything went fine.
511 	- \c B_NO_INIT: The predicate or the volume aren't set.
512 	- \c B_BAD_VALUE: The predicate is invalid.
513 	- \c B_NOT_ALLOWED: Fetch() has already been called.
514 */
515 status_t
516 BQuery::Fetch()
517 {
518 	if (_HasFetched())
519 		return B_NOT_ALLOWED;
520 
521 	_EvaluateStack();
522 
523 	if (!fPredicate || fDevice < 0)
524 		return B_NO_INIT;
525 
526 	fQueryFd = _kern_open_query(fDevice, fPredicate, strlen(fPredicate),
527 		fLive ? B_LIVE_QUERY : 0, fPort, fToken);
528 	if (fQueryFd < 0)
529 		return fQueryFd;
530 
531 	// set close on exec flag
532 	fcntl(fQueryFd, F_SETFD, FD_CLOEXEC);
533 
534 	return B_OK;
535 }
536 
537 
538 // BEntryList interface
539 
540 // GetNextEntry
541 /*!	\brief Returns the BQuery's next entry as a BEntry.
542 	Places the next entry in the list in \a entry, traversing symlinks if
543 	\a traverse is \c true.
544 	\param entry a pointer to a BEntry to be initialized with the found entry
545 	\param traverse specifies whether to follow it, if the found entry
546 		   is a symbolic link.
547 	\note The iterator used by this method is the same one used by
548 		  GetNextRef() and GetNextDirents().
549 	\return
550 	- \c B_OK if successful,
551 	- \c B_ENTRY_NOT_FOUND when at the end of the list,
552 	- \c B_BAD_VALUE: The queries predicate includes unindexed attributes.
553 	- \c B_FILE_ERROR: Fetch() has not been called before.
554 */
555 status_t
556 BQuery::GetNextEntry(BEntry *entry, bool traverse)
557 {
558 	status_t error = (entry ? B_OK : B_BAD_VALUE);
559 	if (error == B_OK) {
560 		entry_ref ref;
561 		error = GetNextRef(&ref);
562 		if (error == B_OK)
563 			error = entry->SetTo(&ref, traverse);
564 	}
565 	return error;
566 }
567 
568 // GetNextRef
569 /*!	\brief Returns the BQuery's next entry as an entry_ref.
570 	Places an entry_ref to the next entry in the list into \a ref.
571 	\param ref a pointer to an entry_ref to be filled in with the data of the
572 		   found entry
573 	\note The iterator used by this method is the same one used by
574 		  GetNextEntry() and GetNextDirents().
575 	\return
576 	- \c B_OK if successful,
577 	- \c B_ENTRY_NOT_FOUND when at the end of the list,
578 	- \c B_BAD_VALUE: The queries predicate includes unindexed attributes.
579 	- \c B_FILE_ERROR: Fetch() has not been called before.
580 */
581 status_t
582 BQuery::GetNextRef(entry_ref *ref)
583 {
584 	status_t error = (ref ? B_OK : B_BAD_VALUE);
585 	if (error == B_OK && !_HasFetched())
586 		error = B_FILE_ERROR;
587 	if (error == B_OK) {
588 		BPrivate::Storage::LongDirEntry entry;
589 		bool next = true;
590 		while (error == B_OK && next) {
591 			if (GetNextDirents(&entry, sizeof(entry), 1) != 1) {
592 				error = B_ENTRY_NOT_FOUND;
593 			} else {
594 				next = (!strcmp(entry.d_name, ".")
595 						|| !strcmp(entry.d_name, ".."));
596 			}
597 		}
598 		if (error == B_OK) {
599 			ref->device = entry.d_pdev;
600 			ref->directory = entry.d_pino;
601 			error = ref->set_name(entry.d_name);
602 		}
603 	}
604 	return error;
605 }
606 
607 // GetNextDirents
608 /*!	\brief Returns the BQuery's next entries as dirent structures.
609 	Reads a number of entries into the array of dirent structures pointed to by
610 	\a buf. Reads as many but no more than \a count entries, as many entries as
611 	remain, or as many entries as will fit into the array at \a buf with given
612 	length \a length (in bytes), whichever is smallest.
613 	\param buf a pointer to a buffer to be filled with dirent structures of
614 		   the found entries
615 	\param length the maximal number of entries to be read.
616 	\note The iterator used by this method is the same one used by
617 		  GetNextEntry() and GetNextRef().
618 	\return
619 	- The number of dirent structures stored in the buffer, 0 when there are
620 	  no more entries to be read.
621 	- \c B_BAD_VALUE: The queries predicate includes unindexed attributes.
622 	- \c B_FILE_ERROR: Fetch() has not been called before.
623 */
624 int32
625 BQuery::GetNextDirents(struct dirent *buf, size_t length, int32 count)
626 {
627 	if (!buf)
628 		return B_BAD_VALUE;
629 	if (!_HasFetched())
630 		return B_FILE_ERROR;
631 	return _kern_read_dir(fQueryFd, buf, length, count);
632 }
633 
634 // Rewind
635 /*!	\brief Rewinds the entry list back to the first entry.
636 
637 	Unlike R5 Haiku implements this method for BQuery.
638 
639 	\return
640 	- \c B_OK on success,
641 	- \c B_FILE_ERROR, if Fetch() has not yet been called.
642 */
643 status_t
644 BQuery::Rewind()
645 {
646 	if (!_HasFetched())
647 		return B_FILE_ERROR;
648 	return _kern_rewind_dir(fQueryFd);
649 }
650 
651 // CountEntries
652 /*!	\brief Unimplemented method of the BEntryList interface.
653 	\return 0.
654 */
655 int32
656 BQuery::CountEntries()
657 {
658 	return B_ERROR;
659 }
660 
661 // _HasFetched
662 /*!	Returns whether Fetch() has already been called on this object.
663 	\return \c true, if Fetch() has successfully been invoked, \c false
664 			otherwise.
665 */
666 bool
667 BQuery::_HasFetched() const
668 {
669 	return (fQueryFd >= 0);
670 }
671 
672 // _PushNode
673 /*!	\brief Pushs a node onto the predicate stack.
674 	If the stack has not been allocate until this time, this method does
675 	allocate it.
676 	If the supplied node is \c NULL, it is assumed that there was not enough
677 	memory to allocate the node and thus \c B_NO_MEMORY is returned.
678 	In case the method fails, the caller retains the ownership of the supplied
679 	node and thus is responsible for deleting it, if \a deleteOnError is
680 	\c false. If it is \c true, the node is deleted, if an error occurs.
681 	\param node the node to be pushed
682 	\param deleteOnError
683 	\return
684 	- \c B_OK: Everything went fine.
685 	- \c B_NO_MEMORY: \c NULL \a node or insuffient memory to allocate the
686 	  predicate stack or push the node.
687 	- \c B_NOT_ALLOWED: _PushNode() was called after Fetch().
688 */
689 status_t
690 BQuery::_PushNode(QueryNode *node, bool deleteOnError)
691 {
692 	status_t error = (node ? B_OK : B_NO_MEMORY);
693 	if (error == B_OK && _HasFetched())
694 		error = B_NOT_ALLOWED;
695 	// allocate the stack, if necessary
696 	if (error == B_OK && !fStack) {
697 		fStack = new(nothrow) QueryStack;
698 		if (!fStack)
699 			error = B_NO_MEMORY;
700 	}
701 	if (error == B_OK)
702 		error = fStack->PushNode(node);
703 	if (error != B_OK && deleteOnError)
704 		delete node;
705 	return error;
706 }
707 
708 // _SetPredicate
709 /*!	\brief Helper method to set the BQuery's predicate.
710 	It is not checked whether Fetch() has already been invoked.
711 	\param predicate the predicate string
712 	\return
713 	- \c B_OK: Everything went fine.
714 	- \c B_NO_MEMORY: Insufficient memory to store the predicate.
715 */
716 status_t
717 BQuery::_SetPredicate(const char *expression)
718 {
719 	status_t error = B_OK;
720 	// unset the old predicate
721 	delete[] fPredicate;
722 	fPredicate = NULL;
723 	// set the new one
724 	if (expression) {
725 		fPredicate = new(nothrow) char[strlen(expression) + 1];
726 		if (fPredicate)
727 			strcpy(fPredicate, expression);
728 		else
729 			error = B_NO_MEMORY;
730 	}
731 	return error;
732 }
733 
734 // _EvaluateStack
735 /*!	Evaluates the query's predicate stack.
736 	The method does nothing (and returns \c B_OK), if the stack is \c NULL.
737 	If the stack is non-null and Fetch() has already been called, the method
738 	fails.
739 	\return
740 	- \c B_OK: Everything went fine.
741 	- \c B_NO_MEMORY: Insufficient memory.
742 	- \c B_NOT_ALLOWED: _EvaluateStack() was called after Fetch().
743 	- another error code
744 */
745 status_t
746 BQuery::_EvaluateStack()
747 {
748 	status_t error = B_OK;
749 	if (fStack) {
750 		_SetPredicate(NULL);
751 		if (_HasFetched())
752 			error = B_NOT_ALLOWED;
753 		// convert the stack to a tree and evaluate it
754 		QueryNode *node = NULL;
755 		if (error == B_OK)
756 			error = fStack->ConvertToTree(node);
757 		BString predicate;
758 		if (error == B_OK)
759 			error = node->GetString(predicate);
760 		if (error == B_OK)
761 			error = _SetPredicate(predicate.String());
762 		delete fStack;
763 		fStack = NULL;
764 	}
765 	return error;
766 }
767 
768 
769 // FBC
770 void BQuery::_QwertyQuery1() {}
771 void BQuery::_QwertyQuery2() {}
772 void BQuery::_QwertyQuery3() {}
773 void BQuery::_QwertyQuery4() {}
774 void BQuery::_QwertyQuery5() {}
775 void BQuery::_QwertyQuery6() {}
776 
777