Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_query_criteria.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW - String Functions
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw_internal.h"
15 #include <libxml/xmlregexp.h>
16 
17 
18 
19 /* ------------------------------------------------------------------------- */
20 /* Internal parse URL encoded RQL string typedef and declares */
21 /* ------------------------------------------------------------------------- */
22 
23 typedef struct impl_string_parser_s {
24  afw_xctx_t *xctx;
25  const afw_pool_t *p;
26  afw_query_criteria_t *criteria;
28  const afw_utf8_z_t *start;
29  const afw_utf8_z_t *c;
30  afw_utf8_t token;
31 
32  /* Token types. */
33  enum {
34  impl_token_type_unset = 0,
35  impl_token_type_delimiter,
36  impl_token_type_operation,
37  impl_token_type_string,
38  impl_token_type_end
39  } token_type;
40 
41  /*
42  * These two flags are needed to deal with '!' not being a delimiter
43  * except when expecting an operation.
44  */
45  afw_boolean_t bang_is_a_delimiter;
46  afw_boolean_t expect_an_operation;
47 
49 
50 static void
51 impl_parse_string_filter(
52  impl_string_parser_t *parser,
55  const afw_query_criteria_filter_entry_t *on_true,
56  const afw_query_criteria_filter_entry_t *on_false);
57 
58 static void
59 impl_parse_string_function(
60  impl_string_parser_t *parser,
63  const afw_query_criteria_filter_entry_t *on_true,
64  const afw_query_criteria_filter_entry_t *on_false);
65 
66 static void
67 impl_parse_string_sugar(
68  impl_string_parser_t *parser,
71  const afw_query_criteria_filter_entry_t *on_true,
72  const afw_query_criteria_filter_entry_t *on_false);
73 
74 static void
75 impl_parse_string_term(
76  impl_string_parser_t *parser,
79  const afw_query_criteria_filter_entry_t *on_true,
80  const afw_query_criteria_filter_entry_t *on_false);
81 
82 static void
83 impl_parse_string_factor(
84  impl_string_parser_t *parser,
87  const afw_query_criteria_filter_entry_t *on_true,
88  const afw_query_criteria_filter_entry_t *on_false);
89 
91 impl_parse_string_relation(
92  impl_string_parser_t *parser,
93  const afw_query_criteria_filter_entry_t *on_true,
94  const afw_query_criteria_filter_entry_t *on_false);
95 
96 static const afw_utf8_t * const *
97 impl_parse_string_select(impl_string_parser_t *parser);
98 
100 impl_parse_string_sort(impl_string_parser_t *parser);
101 
102 static const afw_utf8_t *
103 impl_decode_token(impl_string_parser_t *parser);
104 
105 static const afw_value_t *
106 impl_token_to_value(impl_string_parser_t *parser);
107 
108 static void
109 impl_get_token(impl_string_parser_t *parser);
110 
111 static afw_boolean_t
112 impl_is_delimiter(afw_utf8_octet_t ch);
113 
114 static const afw_value_t *
115 impl_parse_string_list_value(impl_string_parser_t *parser);
116 
117 static void
118 impl_set_alt(afw_query_criteria_filter_entry_t *entry);
119 
120 
121 
122 
123 /* ------------------------------------------------------------------------- */
124 /* Internal parse object typedef and declares */
125 /* ------------------------------------------------------------------------- */
126 
128  afw_xctx_t *xctx;
129  const afw_pool_t *p;
130  afw_query_criteria_t *criteria;
133 
134 static void
135 impl_AdaptiveQueryCriteria_object_parse_filter(
137  const afw_object_t *filter_object,
138  const afw_query_criteria_filter_entry_t **filter,
140  const afw_query_criteria_filter_entry_t *on_true,
141  const afw_query_criteria_filter_entry_t *on_false);
142 
143 static const afw_utf8_t * const *
144 impl_AdaptiveQueryCriteria_object_parse_select(
146  const afw_list_t *select);
147 
148 static const afw_query_criteria_sort_entry_t *
149 impl_AdaptiveQueryCriteria_object_parse_sort(
151  const afw_list_t *sort);
152 
153 
154 /* ------------------------------------------------------------------------- */
155 /* Internal common typedefs an consts */
156 /* ------------------------------------------------------------------------- */
157 
158 
159 typedef struct impl_op_s {
160  const afw_utf8_t *name;
162  afw_boolean_t is_list;
163 } impl_op_t;
164 
165 typedef struct impl_rql_op_s {
166  const afw_utf8_t *name;
167  const impl_op_t *op;
168  afw_boolean_t can_be_property;
169 } impl_rql_op_t;
170 
171 typedef struct impl_fiql_op_s {
172  const afw_utf8_z_t *fiql_z;
173  const impl_op_t *op;
175 
176 
177 static const impl_op_t
178 impl_op_and = {
179  &afw_s_and,
180  afw_query_criteria_filter_op_id_and,
181  false
182 };
183 
184 static const impl_op_t
185 impl_op_or = {
186  &afw_s_or,
187  afw_query_criteria_filter_op_id_or,
188  false
189 };
190 
191 static const impl_op_t
192 impl_op_in = {
193  &afw_s_in,
194  afw_query_criteria_filter_op_id_in,
195  true
196 };
197 
198 static const impl_op_t
199 impl_op_out = {
200  &afw_s_out,
201  afw_query_criteria_filter_op_id_out,
202  true
203 };
204 
205 static const impl_op_t
206 impl_op_contains = {
207  &afw_s_contains,
208  afw_query_criteria_filter_op_id_contains,
209  false
210 };
211 
212 static const impl_op_t
213 impl_op_excludes = {
214  &afw_s_excludes,
215  afw_query_criteria_filter_op_id_excludes,
216  false
217 };
218 
219 static const impl_op_t
220 impl_op_match = {
221  &afw_s_match,
222  afw_query_criteria_filter_op_id_match,
223  false
224 };
225 
226 static const impl_op_t
227 impl_op_differ = {
228  &afw_s_differ,
229  afw_query_criteria_filter_op_id_differ,
230  false
231 };
232 
233 static const impl_op_t
234 impl_op_eq = {
235  &afw_s_eq,
236  afw_query_criteria_filter_op_id_eq,
237  false
238 };
239 
240 static const impl_op_t
241 impl_op_lt = {
242  &afw_s_lt,
243  afw_query_criteria_filter_op_id_lt,
244  false
245 };
246 
247 static const impl_op_t
248 impl_op_le = {
249  &afw_s_le,
250  afw_query_criteria_filter_op_id_le,
251  false
252 };
253 
254 static const impl_op_t
255 impl_op_gt = {
256  &afw_s_gt,
257  afw_query_criteria_filter_op_id_gt,
258  false
259 };
260 
261 static const impl_op_t
262 impl_op_ge = {
263  &afw_s_ge,
264  afw_query_criteria_filter_op_id_ge,
265  false
266 };
267 
268 static const impl_op_t
269 impl_op_ne = {
270  &afw_s_ne,
271  afw_query_criteria_filter_op_id_ne,
272  false
273 };
274 
275 
276 
277 static const impl_rql_op_t
278 impl_rql_op[] = {
279 
280  {
281  &afw_s_and,
282  &impl_op_and,
283  true
284  },
285 
286  {
287  &afw_s_or,
288  &impl_op_or,
289  true
290  },
291 
292  {
293  &afw_s_in,
294  &impl_op_in,
295  true
296  },
297 
298  {
299  &afw_s_out,
300  &impl_op_out,
301  true
302  },
303 
304  {
305  &afw_s_contains,
306  &impl_op_contains,
307  true
308  },
309 
310  {
311  &afw_s_excludes,
312  &impl_op_excludes,
313  true
314  },
315 
316  {
317  &afw_s_match,
318  &impl_op_match,
319  true
320  },
321 
322  {
323  &afw_s_differ,
324  &impl_op_differ,
325  true
326  },
327 
328  {
329  &afw_s_eq,
330  &impl_op_eq,
331  true
332  },
333 
334  {
335  &afw_s_lt,
336  &impl_op_lt,
337  true
338  },
339 
340  {
341  &afw_s_le,
342  &impl_op_le,
343  true
344  },
345 
346  {
347  &afw_s_lte,
348  &impl_op_le,
349  false
350  },
351 
352  {
353  &afw_s_gt,
354  &impl_op_gt,
355  true
356  },
357 
358  {
359  &afw_s_ge,
360  &impl_op_ge,
361  true
362  },
363 
364  {
365  &afw_s_gte,
366  &impl_op_ge,
367  false
368  },
369 
370  {
371  &afw_s_ne,
372  &impl_op_ne,
373  true
374  }
375 };
376 
377 /* Operations that can be in a relation. */
378 static const impl_fiql_op_t
379 impl_fiql_op[] = {
380  {
381  "=",
382  &impl_op_eq
383  },
384  {
385  "==",
386  &impl_op_eq
387  },
388  {
389  "=eq=",
390  &impl_op_eq
391  },
392  {
393  "=ne=",
394  &impl_op_ne
395  },
396  {
397  "!=",
398  &impl_op_ne
399  },
400  {
401  "=lt=",
402  &impl_op_lt
403  },
404  {
405  "=le=",
406  &impl_op_le
407  },
408  {
409  "=lte=",
410  &impl_op_le
411  },
412  {
413  "=gt=",
414  &impl_op_gt
415  },
416  {
417  "=ge=",
418  &impl_op_ge
419  },
420  {
421  "=gte=",
422  &impl_op_ge
423  },
424  {
425  "=match=",
426  &impl_op_match
427  },
428  {
429  "=differ=",
430  &impl_op_differ
431  },
432  {
433  "=contains=",
434  &impl_op_contains
435  },
436  {
437  "=in=",
438  &impl_op_in
439  },
440  {
441  "=out=",
442  &impl_op_out
443  },
444  {NULL}
445 };
446 
447 /* Delimiters. Notice last char in string is a space. */
448 static const afw_utf8_octet_t * impl_delimiter = "&=<>()|,;+# ";
449 
450 
451 /* ------------------------------------------------------------------------- */
452 /* Internal string parser error handling */
453 /* ------------------------------------------------------------------------- */
454 
455 
456 /* Parse error. */
457 static void
458 impl_string_parser_set_error_z(
459  impl_string_parser_t *parser,
460  const afw_utf8_z_t *source_z,
461  const afw_utf8_z_t *message_z)
462 {
463  afw_error_set_fz(afw_error_code_syntax,
464  source_z, parser->xctx,
465  "Query string error at offset +%" AFW_SIZE_T_FMT
466  ": %s",
467  parser->c - parser->start - parser->token.len,
468  message_z);
469 }
470 
471 
472 /* Parse error. */
473 static void
474 impl_string_parser_set_error_fz(
475  impl_string_parser_t *parser,
476  const afw_utf8_z_t *source_z,
477  const afw_utf8_z_t *format_z, ...)
478 {
479  va_list ap;
480  const afw_utf8_z_t *message_z;
481 
482  va_start(ap, format_z);
483  message_z = afw_utf8_z_printf_v(format_z, ap,
484  parser->p, parser->xctx);
485  va_end(ap);
486 
487  impl_string_parser_set_error_z(parser, source_z, message_z);
488 }
489 
490 
491 /* Macro used to set parse error in xctx and throw it. */
492 #define IMPL_STRING_THROW_ERROR_Z(message_z) \
493 do { \
494  impl_string_parser_set_error_z(parser, \
495  AFW__FILE_LINE__, message_z); \
496  longjmp(((parser->xctx)->current_try->throw_jmp_buf), \
497  (afw_error_code_syntax)); \
498 } while (0)
499 
500 
501 /* Macro used to set parse error in xctx and throw it. */
502 #define IMPL_STRING_THROW_ERROR_FZ(format_z, ...) \
503 do { \
504  impl_string_parser_set_error_fz(parser, \
505  AFW__FILE_LINE__, format_z, __VA_ARGS__); \
506  longjmp(((parser->xctx)->current_try->throw_jmp_buf), \
507  (afw_error_code_syntax)); \
508 } while (0)
509 
510 
511 
512 
513 /* ------------------------------------------------------------------------- */
514 /* Internal parse URL encoded RQL string function defines */
515 /* ------------------------------------------------------------------------- */
516 
517 AFW_DEFINE_STATIC_INLINE(afw_boolean_t)
518 impl_token_equal(
519  impl_string_parser_t *parser, const afw_utf8_t *s)
520 {
521  return afw_utf8_equal(&parser->token, s);
522 }
523 
524 
525 AFW_DEFINE_STATIC_INLINE(afw_boolean_t)
526 impl_token_equal_z(
527  impl_string_parser_t *parser, const afw_utf8_z_t *z)
528 {
529  return afw_utf8_equal_utf8_z(&parser->token, z);
530 }
531 
532 
533 AFW_DEFINE_STATIC_INLINE(void)
534 impl_token_copy(
535  impl_string_parser_t *parser, afw_utf8_t *to)
536 {
537  memcpy(to, &parser->token, sizeof(afw_utf8_t));
538 }
539 
540 
541 /* Determine if character is a delimiter (includes 0). */
542 static afw_boolean_t
543 impl_is_delimiter(afw_utf8_octet_t ch)
544 {
545  const afw_utf8_octet_t * delimiter;
546 
547  delimiter = impl_delimiter;
548  while (1) {
549  if (*delimiter == ch) return true;
550  if (*delimiter == 0) break;
551  delimiter++;
552  }
553 
554  return false;
555 }
556 
557 
558 /* Get next token. Token pointer and size are set in parser. */
559 static void
560 impl_get_token(impl_string_parser_t *parser)
561 {
562  const afw_utf8_octet_t *c;
563 
564  /* Catch if being called after end. */
565  if (parser->token_type == impl_token_type_end) {
566  IMPL_STRING_THROW_ERROR_Z(
567  "call to impl_get_token() after end");
568  }
569 
570  /* Set token start. */
571  parser->token.s = parser->c;
572 
573  /* If at end, token length is 0 and return. */
574  if (*(parser->c) == 0 || *(parser->c) == '#') {
575  parser->token.len = 0;
576  parser->token_type = impl_token_type_end;
577  return;
578  }
579 
580  /*
581  * If next char is '=', token is either '=' or an operator enclose in '='s.
582  * Set token size accordingly to include '='s. See impl_fiql_op[].
583  */
584  if (parser->expect_an_operation &&
585  (*(parser->c) == '=' || *(parser->c) == '!'))
586  {
587  parser->token_type = impl_token_type_operation;
588  parser->token.len = 1;
589  c = parser->c + 1;
590  if (*(parser->c) == '=') {
591  while (1) {
592  if (*c == '=') {
593  parser->token.len = c - parser->c + 1;
594  break;
595  }
596  if (impl_is_delimiter(*c)) break;
597  c++;
598  }
599  }
600  else {
601  if (*c == '=') {
602  parser->token.len = 2;
603  }
604  }
605  }
606 
607  /* Else if cursor points to a delimiter, set token size to 1. */
608  else if (impl_is_delimiter(*(parser->c))) {
609  parser->token_type = impl_token_type_delimiter;
610  parser->token.len = 1;
611  }
612 
613  /* Else token is characters up to next delimiter. */
614  else {
615  for (c = parser->c;
616  !impl_is_delimiter(*c) &&
617  (!parser->bang_is_a_delimiter || *c != '!');
618  c++);
619  parser->token.len = c - parser->c;
620  parser->token_type = impl_token_type_string;
621  }
622 
623  /* Update cursor past token. */
624  parser->c += parser->token.len;
625 
626  /* Reset bang_is_a_delimiter and expect_an_operation. */
627  parser->bang_is_a_delimiter = false;
628  parser->expect_an_operation = false;
629 }
630 
631 
632 static const afw_utf8_t *
633 impl_decode_token(impl_string_parser_t *parser) {
634  return afw_uri_decode_create(parser->token.s, parser->token.len,
635  parser->p, parser->xctx);
636 }
637 
638 
639 static const afw_value_t *
640 impl_token_to_value(impl_string_parser_t *parser)
641 {
642  const afw_utf8_t *decoded;
643 
644  decoded = afw_uri_decode_create(parser->token.s, parser->token.len,
645  parser->p, parser->xctx);
646 
647  return afw_value_create_string(decoded, parser->p, parser->xctx);
648 }
649 
650 
651 static void
652 impl_set_alt(afw_query_criteria_filter_entry_t *entry)
653 {
654  entry->alt_op_id = entry->op_id;
655  entry->alt_not = true;
656  switch (entry->op_id) {
657 
658  case afw_query_criteria_filter_op_id_ne:
659  entry->alt_op_id = afw_query_criteria_filter_op_id_eq;
660  break;
661 
662  case afw_query_criteria_filter_op_id_ge:
663  entry->alt_op_id = afw_query_criteria_filter_op_id_lt;
664  break;
665 
666  case afw_query_criteria_filter_op_id_gt:
667  entry->alt_op_id = afw_query_criteria_filter_op_id_le;
668  break;
669 
670  case afw_query_criteria_filter_op_id_differ:
671  entry->alt_op_id = afw_query_criteria_filter_op_id_match;
672  break;
673 
674  case afw_query_criteria_filter_op_id_excludes:
675  entry->alt_op_id = afw_query_criteria_filter_op_id_contains;
676  break;
677 
678  case afw_query_criteria_filter_op_id_out:
679  entry->alt_op_id = afw_query_criteria_filter_op_id_in;
680  break;
681 
682  case afw_query_criteria_filter_op_id_and:
683  case afw_query_criteria_filter_op_id_or:
684  entry->alt_op_id = afw_query_criteria_filter_op_id_na;
685  default:
686  entry->alt_not = false;
687  }
688 }
689 
690 
691 /* Parse sort */
692 static const afw_query_criteria_sort_entry_t *
693 impl_parse_string_sort(impl_string_parser_t *parser)
694 {
696  afw_query_criteria_sort_entry_t *previous_entry = NULL;
697  afw_query_criteria_sort_entry_t *first_entry = NULL;
698 
699  /* Loop processing all sort entries. */
700  while (1) {
701 
702  /* Allocate memory for entry. */
703  entry = afw_pool_calloc_type(parser->p,
705  parser->xctx);
706 
707  /* Get next token. */
708  impl_get_token(parser);
709 
710  /* If the token is '-' or '+', indicate in entry and adjust token. */
711  if (*(parser->token.s) == '-') {
712  entry->descending = true;
713  (parser->token.s)++;
714  (parser->token.len)--;
715  }
716  else if (*(parser->token.s) == '+') {
717  impl_get_token(parser);
718  }
719 
720  /* If encoded '+' or '-', indicate in entry and adjust token. */
721  else if (*(parser->token.s) == '%' &&
722  parser->token.len > 3 &&
723  *(parser->token.s + 1) == '2')
724  {
725  /* Encoded '+' */
726  if (*(parser->token.s + 2) == 'b' ||
727  *(parser->token.s + 2) == 'B')
728  {
729  parser->token.s += 3;
730  parser->token.len -= 3;
731  }
732 
733  /* Encoded '-' */
734  else if (*(parser->token.s + 2) == 'd' ||
735  *(parser->token.s + 2) == 'D')
736  {
737  entry->descending = true;
738  parser->token.s += 3;
739  parser->token.len -= 3;
740  }
741  }
742 
743  /* It's an error if token is not a string. */
744  if (parser->token_type != impl_token_type_string) {
745  IMPL_STRING_THROW_ERROR_Z("Invalid sort value");
746  }
747 
748  /* Get property name from token. */
749  entry->property_name = impl_decode_token(parser);
750  if (parser->criteria->object_type && entry->property_name) {
752  parser->criteria->object_type,
753  entry->property_name, parser->xctx);
754  if (!entry->pt || !entry->pt->allow_query) {
755  IMPL_STRING_THROW_ERROR_FZ(
756  "Property %" AFW_UTF8_FMT " cannot be queried",
757  AFW_UTF8_FMT_ARG(entry->property_name));
758  }
759  }
760 
763  /* Set first and previous entry pointer. */
764  if (!previous_entry) {
765  first_entry = entry;
766  }
767  else {
768  previous_entry->next = entry;
769  }
770  previous_entry = entry;
771 
772  /*
773  * Get next token. If it is ')', then finished. If ',' or ' ',
774  * then continue. Anything else is an error.
775  */
776  impl_get_token(parser);
777  if (*(parser->token.s) == ')') break;
778  if (*(parser->token.s) == ',' || *(parser->token.s) == ' ') continue;
779  IMPL_STRING_THROW_ERROR_Z("Invalid sort value");
780  }
781 
782  /* Return first entry. */
783  return first_entry;
784 }
785 
786 
787 /* Parse select */
788 static const afw_utf8_t * const *
789 impl_parse_string_select(impl_string_parser_t *parser)
790 {
792  const afw_utf8_t *property_name;
793  static const afw_utf8_t * const *result;
794 
795  /* Make an array to hold zero terminated list of property names. */
796  names = afw_stack_create(afw_const_utf8_a_stack_t, 10, 0, true,
797  parser->p, parser->xctx);
798 
799  /* Loop processing all select entries. */
800  while (1) {
801 
802  /* Get a token. It must be a string. */
803  impl_get_token(parser);
804  if (parser->token_type != impl_token_type_string) {
805  IMPL_STRING_THROW_ERROR_Z("Invalid property name");
806  }
807 
808  /* Get copy of property name from token and push pointer on array. */
809  property_name = impl_decode_token(parser);
811  afw_stack_push(names, parser->xctx) = property_name;
812 
813  /*
814  * Get next token. If it is ')', then finished. If ',' or ' ',
815  * then continue. Anything else is an error.
816  */
817  impl_get_token(parser);
818  if (*(parser->token.s) == ')') break;
819  if (*(parser->token.s) == ',' || *(parser->token.s) == ' ') continue;
820  IMPL_STRING_THROW_ERROR_Z("Invalid select value");
821  }
822 
823  /* Return NULL terminated list of select names. */
824  afw_stack_push(names, parser->xctx) = NULL;
825 
826  /* Get copy of stack, release instance, and return copy. */
827  afw_stack_copy_and_release(names, NULL, &result,
828  parser->p, parser->xctx);
829  return result;
830 }
831 
832 
833 /* Parse relation list value up to ')'. */
834 static const afw_value_t *
835 impl_parse_string_list_value(impl_string_parser_t *parser)
836 {
837  afw_utf8_t sign;
838  apr_array_header_t *values;
839  const afw_utf8_t *value;
840  const afw_list_t *list;
841  const afw_value_t *result;
842 
844  /* Make an array to hold zero terminated list of values. */
845  values = apr_array_make(afw_pool_get_apr_pool(parser->p),
846  10, sizeof(afw_utf8_t));
847 
848  /* Loop though list values. */
849  while (1) {
850 
851  /* Get next token. */
852  impl_get_token(parser);
853 
854  /* If token is '+' or '-', remember sign and get another token. */
855  memset(&sign, 0, sizeof(afw_utf8_t));
856  if (*(parser->token.s) == '+' || *(parser->token.s) == '-') {
857  sign.s = parser->token.s;
858  sign.len = parser->token.len;
859  impl_get_token(parser);
860  }
861 
862  /* Token should be a string. */
863  if (parser->token_type != impl_token_type_string) {
864  IMPL_STRING_THROW_ERROR_Z("Invalid list item in relation");
865  }
866 
867  /*
868  * Get copy of value from token and push pointer on array. If there
869  * is a sign, add it to front of value.
870  */
871  value = impl_decode_token(parser);
872  if (sign.s) {
873  value = afw_utf8_concat(parser->p, parser->xctx, &sign, value, NULL);
874  }
875  memcpy(apr_array_push(values), value, sizeof(afw_utf8_t));
876 
877  /* Get next token. */
878  impl_get_token(parser);
879 
880  /* If one of the separator, continue. */
881  if (*(parser->token.s) == ',')
882  {
883  continue;
884  }
885 
886  /* If next token will be ')', increment cursor and break. */
887  if (*(parser->token.s) == ')') {
888  break;
889  }
890  }
891 
892  /* Return list of strings. */
894  values->elts, false, afw_data_type_string,
895  values->nelts, parser->p, parser->xctx);
896  result = afw_value_create_list(list, parser->p, parser->xctx);
897  return result;
898 }
899 
900 
901 /* Parse relation */
903 impl_parse_string_relation(
904  impl_string_parser_t *parser,
905  const afw_query_criteria_filter_entry_t *on_true,
906  const afw_query_criteria_filter_entry_t *on_false)
907 {
909  const impl_fiql_op_t *o;
910  const afw_utf8_t *decoded = NULL;
911  const xmlChar *s_z;
912 
913  /* Allocate memory for entry */
915  parser->xctx);
916  if (parser->last) {
917  parser->last->next = entry;
918  }
919  parser->last = entry;
920 
921  /* Set on_true and on_false in entry to parameter values. */
922  entry->on_true = on_true;
923  entry->on_false = on_false;
924 
925  /* Get property name and check if query allowed. */
926  parser->bang_is_a_delimiter = true;
927  impl_get_token(parser);
928  if (parser->token_type != impl_token_type_string) {
929  IMPL_STRING_THROW_ERROR_Z(
930  "Invalid or missing property name in relation");
931  }
932  entry->property_name = impl_decode_token(parser);
933  if (parser->criteria->object_type && entry->property_name) {
935  parser->criteria->object_type,
936  entry->property_name, parser->xctx);
937  if (!entry->pt || !entry->pt->allow_query) {
938  IMPL_STRING_THROW_ERROR_FZ(
939  "Property %" AFW_UTF8_FMT " cannot be queried",
940  AFW_UTF8_FMT_ARG(entry->property_name));
941  }
942  }
943 
944  /* Get operation. Operation should not be URL encoded. */
945  parser->expect_an_operation = true;
946  impl_get_token(parser);
947  if (parser->token_type != impl_token_type_operation) {
948  IMPL_STRING_THROW_ERROR_Z(
949  "Invalid or missing operation in relation");
950  }
951 
952  /* Find entry in impl_fiql_op[] for operation. */
953  for (o = &impl_fiql_op[0]; o->fiql_z; o++) {
954  if (strlen(o->fiql_z) == parser->token.len &&
955  memcmp(parser->token.s, o->fiql_z, parser->token.len) == 0)
956  {
957  break;
958  }
959  }
960  if (!o->fiql_z) {
961  IMPL_STRING_THROW_ERROR_Z(
962  "Invalid operation in relation");
963  }
964  entry->op_name = o->op->name;
965  entry->op_id = o->op->op_id;
966  impl_set_alt(entry);
967 
968  /* Single value */
969  if (!o->op->is_list) {
970  impl_get_token(parser);
971  if (parser->token_type != impl_token_type_string) {
972  IMPL_STRING_THROW_ERROR_Z("Invalid value in relation");
973  }
974  decoded = impl_decode_token(parser);
975  entry->value = afw_value_create_string(
976  decoded, parser->p, parser->xctx);
977  }
978 
979  /* List value. */
980  else {
981  /* Get token and expect it to be '(' */
982  impl_get_token(parser);
983  if (*(parser->token.s) != '(') {
984  IMPL_STRING_THROW_ERROR_Z("Expecting '(' in relation");
985  }
986  entry->value = impl_parse_string_list_value(parser);
987  }
988 
989  /* If match, compile expression. */
990  if (entry->op_id == afw_query_criteria_filter_op_id_match) {
991  s_z = BAD_CAST afw_utf8_to_utf8_z(decoded, parser->p, parser->xctx);
992  entry->op_specific = xmlRegexpCompile(s_z);
993  if (entry->op_specific == NULL) {
994  IMPL_STRING_THROW_ERROR_Z("regexp syntax error");
995  }
996  }
997 
998  /* Return entry. */
999  return entry;
1000 }
1001 
1002 
1003 /* Parse factor */
1004 static void
1005 impl_parse_string_factor(
1006  impl_string_parser_t *parser,
1009  const afw_query_criteria_filter_entry_t *on_true,
1010  const afw_query_criteria_filter_entry_t *on_false)
1011 {
1012  if (*(parser->c) == '(') {
1013  (parser->c)++;
1014  impl_parse_string_sugar(parser, filter, tree,
1015  on_true, on_false);
1016  if (*(parser->c) != ')') {
1017  IMPL_STRING_THROW_ERROR_Z("missing ')'");
1018  }
1019  (parser->c)++;
1020  }
1021  else {
1022  *filter = impl_parse_string_relation(parser, on_true, on_false);
1023  *tree = *filter;
1024  }
1025 }
1026 
1027 
1028 static void
1029 impl_patch_on(
1030  afw_query_criteria_filter_entry_t *start_entry,
1031  const afw_query_criteria_filter_entry_t *old_on,
1033 {
1034 
1036 
1037  for (entry = (afw_query_criteria_filter_entry_t *)start_entry;
1038  entry && entry != new_on;
1039  entry = (afw_query_criteria_filter_entry_t *)entry->next)
1040  {
1041  if (entry->on_true == old_on) {
1042  entry->on_true = new_on;
1043  }
1044  if (entry->on_false == old_on) {
1045  entry->on_false = new_on;
1046  }
1047  }
1048 
1049 }
1050 
1051 
1052 /* Parse term */
1053 static void
1054 impl_parse_string_term(
1055  impl_string_parser_t *parser,
1058  const afw_query_criteria_filter_entry_t *on_true,
1059  const afw_query_criteria_filter_entry_t *on_false)
1060 {
1062  afw_query_criteria_filter_entry_t *previous_entry;
1064  afw_query_criteria_filter_entry_t *previous_normalized;
1066 
1067  impl_parse_string_factor(parser, filter, tree, on_true, on_false);
1068 
1069  if (*(parser->c) == '&' || *(parser->c) == ';') {
1070  new_entry = *filter;
1071  and_entry = afw_pool_calloc_type(parser->p,
1073  parser->xctx);
1074  and_entry->op_name = &afw_s_and;
1075  and_entry->op_id = afw_query_criteria_filter_op_id_and;
1076  and_entry->first_conjunctive_child = *tree;
1077  new_tree = *tree;
1078  *tree = and_entry;
1079  do {
1080  (parser->c)++;
1081  previous_entry = new_entry;
1082  previous_normalized = new_tree;
1083  impl_parse_string_factor(parser, &new_entry, &new_tree,
1084  on_true, on_false);
1085  impl_patch_on(previous_entry, on_true, new_entry);
1086  previous_normalized->next_conjunctive_sibling = new_tree;
1087  } while (*(parser->c) == '&' || *(parser->c) == ';');
1088  }
1089 }
1090 
1091 
1092 /* Parse sugar */
1093 static void
1094 impl_parse_string_sugar(
1095  impl_string_parser_t *parser,
1098  const afw_query_criteria_filter_entry_t *on_true,
1099  const afw_query_criteria_filter_entry_t *on_false)
1100 {
1102  afw_query_criteria_filter_entry_t *previous_entry;
1104  afw_query_criteria_filter_entry_t *previous_normalized;
1106 
1107  impl_parse_string_term(parser, filter, tree, on_true, on_false);
1108 
1109  if (*(parser->c) == '|' || *(parser->c) == ',') {
1110  new_entry = *filter;
1111  or_entry = afw_pool_calloc_type(
1112  parser->p,
1114  parser->xctx);
1115  or_entry->op_name = &afw_s_or;
1116  or_entry->op_id = afw_query_criteria_filter_op_id_or;
1117  or_entry->first_conjunctive_child = *tree;
1118  new_tree = *tree;
1119  *tree = or_entry;
1120  do {
1121  (parser->c)++;
1122  previous_entry = new_entry;
1123  previous_normalized = new_tree;
1124  impl_parse_string_term(parser, &new_entry, &new_tree,
1125  on_true, on_false);
1126  impl_patch_on(previous_entry, on_false, new_entry);
1127  previous_normalized->next_conjunctive_sibling = new_tree;
1128  } while (*(parser->c) == '|' || *(parser->c) == ',');
1129  }
1130 }
1131 
1132 
1133 static const impl_rql_op_t *
1134 impl_find_rql_op(const afw_utf8_t *op_name)
1135 {
1136  const impl_rql_op_t *result;
1137 
1138  for (
1139  result = &impl_rql_op[0];
1140  (char *)result < (char *)&impl_rql_op[0] + sizeof(impl_rql_op);
1141  result++)
1142  {
1143  if (afw_utf8_equal(op_name, result->name)) {
1144  return result;
1145  }
1146  }
1147 
1148  return NULL;
1149 }
1150 
1151 
1152 /* Parse tree */
1153 static void
1154 impl_parse_string_function(
1155  impl_string_parser_t *parser,
1158  const afw_query_criteria_filter_entry_t *on_true,
1159  const afw_query_criteria_filter_entry_t *on_false)
1160 {
1161  const impl_rql_op_t *rql_op;
1163  afw_query_criteria_filter_entry_t *child_entry;
1165  afw_query_criteria_filter_entry_t *previous_entry;
1166  afw_query_criteria_filter_entry_t *previous_tree;
1167  const afw_list_t *list;
1168 
1169  /* Find impl_rql_op_t for this operator. */
1170  impl_get_token(parser);
1171  rql_op = impl_find_rql_op(&parser->token);
1172  if (!rql_op) {
1173  IMPL_STRING_THROW_ERROR_FZ(
1174  "Operator %" AFW_UTF8_FMT " is not valid",
1175  AFW_UTF8_FMT_ARG(&parser->token));
1176  }
1177 
1178  /* Next must be an '('. */
1179  impl_get_token(parser);
1180  if (!impl_token_equal_z(parser, "(")) {
1181  IMPL_STRING_THROW_ERROR_Z("Expecting '(' after operator");
1182  }
1183 
1184  /* Allocate and initialize new filter entry. */
1185  entry = afw_pool_calloc_type(
1186  parser->p,
1188  parser->xctx);
1189  *tree = entry;
1190  if (parser->last) {
1191  parser->last->next = entry;
1192  }
1193  parser->last = entry;
1194  entry->op_name = rql_op->op->name;
1195  entry->op_id = rql_op->op->op_id;
1196  impl_set_alt(entry);
1197 
1198  /* Process and/or. */
1199  if (entry->op_id == afw_query_criteria_filter_op_id_and ||
1200  entry->op_id == afw_query_criteria_filter_op_id_or)
1201  {
1202  for (
1203  previous_entry = previous_tree = NULL
1204  ;
1205  ; previous_entry = child_entry, previous_tree = child_tree)
1206  {
1207  impl_parse_string_function(parser,
1208  &child_entry,
1209  &child_tree,
1210  on_true, on_false);
1211 
1212  if (!previous_entry) {
1213  entry->first_conjunctive_child = child_tree;
1214  *filter = child_entry;
1215  }
1216  else {
1217  previous_tree->next_conjunctive_sibling = child_tree;
1218 
1219  /* If "and", patch previous entry true to new entry. */
1220  if (entry->op_id == afw_query_criteria_filter_op_id_and) {
1221  impl_patch_on(previous_entry, previous_entry->on_true,
1222  child_entry);
1223  }
1224 
1225  /* If "or", patch previous entry false to new entry. */
1226  else {
1227  impl_patch_on(previous_entry, previous_entry->on_false,
1228  child_entry);
1229  }
1230  }
1231 
1232  /* Next token is ',' to continue or ')' to end. */
1233  impl_get_token(parser);
1234  if (impl_token_equal_z(parser, ")")) {
1235  break;
1236  }
1237  if (!impl_token_equal_z(parser, ",")) {
1238  IMPL_STRING_THROW_ERROR_Z("Expecting ','");
1239  }
1240  }
1241  }
1242 
1243  /* Process comparisons. */
1244  else {
1245  entry->on_true = on_true;
1246  entry->on_false = on_false;
1247  *filter = entry;
1248 
1249  /* Get property name and make sure query allowed. */
1250  impl_get_token(parser);
1251  if (parser->token_type != impl_token_type_string) {
1252  IMPL_STRING_THROW_ERROR_Z(
1253  "Expecting property name as first parameter of operator");
1254  }
1255  entry->property_name = impl_decode_token(parser);
1256  if (parser->criteria->object_type && entry->property_name) {
1258  parser->criteria->object_type,
1259  entry->property_name, parser->xctx);
1260  if (!entry->pt || !entry->pt->allow_query) {
1261  IMPL_STRING_THROW_ERROR_FZ(
1262  "Property %" AFW_UTF8_FMT " cannot be queried",
1263  AFW_UTF8_FMT_ARG(entry->property_name));
1264  }
1265  }
1266 
1267  /* Get ',' */
1268  impl_get_token(parser);
1269  if (!impl_token_equal_z(parser, ",")) {
1270  IMPL_STRING_THROW_ERROR_Z("Expecting ','");
1271  }
1272 
1273  /* Single value */
1274  if (!rql_op->op->is_list) {
1275  impl_get_token(parser);
1276  if (parser->token_type != impl_token_type_string) {
1277  IMPL_STRING_THROW_ERROR_Z("Invalid value in relation");
1278  }
1279  entry->value = impl_token_to_value(parser);
1280  impl_get_token(parser);
1281  }
1282 
1283  /* List value. */
1284  else {
1285  list = afw_list_create_generic(parser->p, parser->xctx);
1286  entry->value = afw_value_create_list(list,
1287  parser->p, parser->xctx);
1288  for (;;) {
1289  impl_get_token(parser);
1290  if (parser->token_type != impl_token_type_string) {
1291  IMPL_STRING_THROW_ERROR_Z("Expecting string value");
1292  }
1293  afw_list_add_value(list,
1294  impl_token_to_value(parser), parser->xctx);
1295 
1296  impl_get_token(parser);
1297  if (impl_token_equal_z(parser, ")")) {
1298  break;
1299  }
1300  if (!impl_token_equal_z(parser, ",")) {
1301  IMPL_STRING_THROW_ERROR_Z("Expecting ','");
1302  }
1303  }
1304 
1305  /* Make sure it ends with ')' */
1306  if (!impl_token_equal_z(parser, ")")) {
1307  IMPL_STRING_THROW_ERROR_Z("Expecting ')'");
1308  }
1309  }
1310  }
1311 }
1312 
1313 
1314 /* Parse filter */
1315 static void
1316 impl_parse_string_filter(
1317  impl_string_parser_t *parser,
1320  const afw_query_criteria_filter_entry_t *on_true,
1321  const afw_query_criteria_filter_entry_t *on_false)
1322 {
1323  const afw_utf8_z_t *c;
1324 
1325  /* Save cursor. */
1326  c = parser->c;
1327 
1328  /*
1329  * If next tokens are a string followed by '(', restore cursor and parser
1330  * tree.
1331  */
1332  impl_get_token(parser);
1333  if (parser->token_type == impl_token_type_string) {
1334  impl_get_token(parser);
1335  if (afw_utf8_equal_utf8_z(&parser->token, "(")) {
1336  parser->c = c;
1337  impl_parse_string_function(parser, filter, tree,
1338  on_true, on_false);
1339  return;
1340  }
1341  }
1342 
1343  /* Otherwise, restore cursor and parser sugar version. */
1344  parser->c = c;
1345  impl_parse_string_sugar(parser, filter, tree, on_true, on_false);
1346 }
1347 
1348 
1349 /* ------------------------------------------------------------------------- */
1350 /* Internal parse object function defines */
1351 /* ------------------------------------------------------------------------- */
1352 
1353 static void
1354 impl_AdaptiveQueryCriteria_object_parse_filter(
1356  const afw_object_t *filter_object,
1357  const afw_query_criteria_filter_entry_t **filter,
1358  const afw_query_criteria_filter_entry_t **tree,
1359  const afw_query_criteria_filter_entry_t *on_true,
1360  const afw_query_criteria_filter_entry_t *on_false)
1361 {
1362  const impl_rql_op_t *rql_op;
1364  afw_query_criteria_filter_entry_t *child_entry;
1366  afw_query_criteria_filter_entry_t *previous_entry;
1367  afw_query_criteria_filter_entry_t *previous_tree;
1368  const afw_iterator_t *iterator;
1369  const afw_list_t *filters_list;
1370  const afw_object_t *child_object;
1371  const afw_value_t *value;
1372  const afw_utf8_t *s;
1373 
1374  /* Get "op" property and get its corresponding operator. */
1375  s = afw_object_old_get_property_as_string(filter_object,
1376  &afw_s_op, parser->xctx);
1377  if (!s) {
1378  AFW_THROW_ERROR_Z(general, "Missing \"op\" property", parser->xctx);
1379  }
1380  rql_op = impl_find_rql_op(s);
1381  if (!rql_op) {
1382  AFW_THROW_ERROR_FZ(general, parser->xctx,
1383  "Property \"op\" value is not valid: %" AFW_UTF8_FMT,
1384  AFW_UTF8_FMT_ARG(s));
1385  }
1386 
1387  /* Allocate and initialize new filter entry. */
1388  entry = afw_pool_calloc_type(
1389  parser->p,
1391  parser->xctx);
1392  *tree = entry;
1393  if (parser->last) {
1394  parser->last->next = entry;
1395  }
1396  parser->last = entry;
1397  entry->op_name = rql_op->op->name;
1398  entry->op_id = rql_op->op->op_id;
1399  impl_set_alt(entry);
1400 
1401  /* Process and/or. */
1402  if (entry->op_id == afw_query_criteria_filter_op_id_and ||
1403  entry->op_id == afw_query_criteria_filter_op_id_or)
1404  {
1405  filters_list = afw_object_old_get_property_as_list(filter_object,
1406  &afw_s_filters, parser->xctx);
1407  if (!filters_list) {
1408  AFW_THROW_ERROR_Z(general,
1409  "Property \"filters\" for this op",
1410  parser->xctx);
1411  }
1412 
1413  for (
1414  previous_entry = previous_tree = NULL,
1415  iterator = NULL
1416  ;
1417  ; previous_entry = child_entry, previous_tree = child_tree)
1418  {
1419  value = afw_list_get_next_value(filters_list, &iterator,
1420  parser->p, parser->xctx);
1421  if (!value) {
1422  break;
1423  }
1424  if (!afw_value_is_object(value)) {
1425  AFW_THROW_ERROR_Z(general,
1426  "Property \"filters\" list entries must be objects",
1427  parser->xctx);
1428  }
1429  child_object = afw_value_as_object(value, parser->xctx);
1430 
1431  impl_AdaptiveQueryCriteria_object_parse_filter(
1432  parser, child_object,
1433  (const afw_query_criteria_filter_entry_t **)&child_entry,
1434  (const afw_query_criteria_filter_entry_t **)&child_tree,
1435  on_true, on_false);
1436 
1437  if (!previous_entry) {
1438  entry->first_conjunctive_child = child_tree;
1439  *filter = child_entry;
1440  }
1441  else {
1442  previous_tree->next_conjunctive_sibling = child_tree;
1443 
1444  /* If "and", patch previous entry true to new entry. */
1445  if (entry->op_id == afw_query_criteria_filter_op_id_and) {
1446  impl_patch_on(previous_entry, previous_entry->on_true,
1447  child_entry);
1448  }
1449 
1450  /* If "or", patch previous entry false to new entry. */
1451  else {
1452  impl_patch_on(previous_entry, previous_entry->on_false,
1453  child_entry);
1454  }
1455  }
1456  }
1457  }
1458 
1459  /* Process comparisons. */
1460  else {
1461  entry->on_true = on_true;
1462  entry->on_false = on_false;
1463  *filter = entry;
1464  entry->property_name = afw_object_old_get_property_as_string(
1465  filter_object, &afw_s_property, parser->xctx);
1466  if (parser->criteria->object_type && entry->property_name) {
1468  parser->criteria->object_type,
1469  entry->property_name, parser->xctx);
1470  if (!entry->pt || !entry->pt->allow_query) {
1471  AFW_THROW_ERROR_FZ(general, parser->xctx,
1472  "Property %" AFW_UTF8_FMT " cannot be queried",
1473  AFW_UTF8_FMT_ARG(entry->property_name));
1474  }
1475  }
1476 
1477  entry->value = afw_object_get_property(
1478  filter_object, &afw_s_value, parser->xctx);
1480  }
1481 }
1482 
1483 
1484 static const afw_utf8_t * const *
1485 impl_AdaptiveQueryCriteria_object_parse_select(
1487  const afw_list_t *select)
1488 {
1489  afw_const_utf8_a_stack_t *names;
1490  const afw_utf8_t *name;
1491  const afw_utf8_t * const *result;
1492  const afw_iterator_t *iterator;
1493  const afw_value_t *value;
1494 
1495  names = afw_stack_create(afw_const_utf8_a_stack_t, 20, 0, true,
1496  parser->p, parser->xctx);
1497  for (iterator = NULL;;) {
1498  value = afw_list_get_next_value(select, &iterator,
1499  parser->p, parser->xctx);
1500  if (!value) {
1501  break;
1502  }
1503  name = afw_value_as_string(value, parser->xctx);
1504  afw_stack_push(names, parser->xctx) = name;
1505  }
1506  afw_stack_push(names, parser->xctx) = NULL;
1507 
1508  /* Get copy of stack, release instance, and return copy. */
1509  afw_stack_copy_and_release(names, NULL, &result,
1510  parser->p, parser->xctx);
1511  return result;
1512 }
1513 
1514 
1515 static const afw_query_criteria_sort_entry_t *
1516 impl_AdaptiveQueryCriteria_object_parse_sort(
1518  const afw_list_t *sort)
1519 {
1523  const afw_utf8_t *name;
1524  const afw_iterator_t *iterator;
1525  const afw_value_t *value;
1526 
1527  for (iterator = NULL, result = NULL, curr = NULL;;)
1528  {
1529  value = afw_list_get_next_value(sort, &iterator,
1530  parser->p, parser->xctx);
1531  if (!value) {
1532  break;
1533  }
1534  prev = curr;
1535  curr = afw_pool_calloc_type(parser->p,
1536  afw_query_criteria_sort_entry_t, parser->xctx);
1537  if (prev) {
1538  prev->next = curr;
1539  }
1540  else {
1541  result = curr;
1542  }
1543  name = afw_value_as_string(value, parser->xctx);
1544  if (name->len <= 2 ||
1545  (*name->s != '+' && *name->s != '-')
1546  )
1547  {
1548  AFW_THROW_ERROR_Z(general,
1549  "sort property name is missing or does not start with "
1550  "'+' or '-'",
1551  parser->xctx);
1552  }
1553  curr->descending = *name->s == '-';
1554  curr->property_name = afw_utf8_create(name->s + 1, name->len - 1,
1555  parser->p, parser->xctx);
1556  }
1557  return result;
1558 }
1559 
1560 
1561 
1562 /* ------------------------------------------------------------------------- */
1563 /* Internal query test function defines */
1564 /* ------------------------------------------------------------------------- */
1565 
1566 static afw_boolean_t
1567 impl_compare_value(
1568  const afw_query_criteria_filter_entry_t *entry,
1569  const afw_value_t *value,
1570  const afw_pool_t *p,
1571  afw_xctx_t *xctx)
1572 {
1573  afw_boolean_t is_true;
1574  const void *i1, *i2;
1575  const afw_iterator_t *iterator;
1576  const afw_value_t *entry_value;
1577  const afw_data_type_t *data_type;
1578  const afw_data_type_t *entry_data_type;
1579  const afw_list_t *list;
1580  afw_list_wrapper_for_array_self_t list_for_single_internal;
1581 
1582  /*
1583  * If not operator contains or match and data type passed does not match,
1584  * try to convert property value to entry type.
1585  */
1586  entry_value = entry->value;
1587  if (entry->op_id != afw_query_criteria_filter_op_id_match &&
1588  entry->op_id != afw_query_criteria_filter_op_id_contains)
1589  {
1590  if (value->inf != entry_value->inf) {
1591  AFW_TRY {
1592  entry_value = afw_value_convert(entry_value,
1593  afw_value_get_data_type(value, xctx), false,
1594  p, xctx);
1595  }
1597  entry_value = NULL;
1598  }
1599  AFW_ENDTRY;
1600  if (!entry_value) {
1601  return false;
1602  }
1603  }
1604  }
1605 
1606  /* Set up so process operation can always work with a list. */
1607  if (afw_value_is_list(value)) {
1608  list = ((afw_value_list_t *)value)->internal;
1609  data_type = afw_list_get_data_type(list, xctx);
1611  }
1612  else {
1613  data_type = afw_value_get_data_type(value, xctx);
1615  &list_for_single_internal,
1616  &((afw_value_evaluated_t *)value)->internal, false,
1617  data_type, 1);
1618  list = (afw_list_t *)&list_for_single_internal;
1619  }
1620 
1621  /* Process operation. */
1622  switch (entry->op_id) {
1623 
1624  case afw_query_criteria_filter_op_id_eq:
1625  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1626  for (is_true = false, iterator = NULL;;) {
1627  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1628  if (!i2) {
1629  break;
1630  }
1631  if (data_type != entry_data_type) {
1632  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1633  }
1635  afw_value_get_data_type(value, xctx),
1636  i1, i2, xctx) == 0;
1637  if (is_true) {
1638  break;
1639  }
1640  }
1641  break;
1642 
1643  case afw_query_criteria_filter_op_id_ne:
1644  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1645  for (is_true = true, iterator = NULL;;) {
1646  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1647  if (!i2) {
1648  break;
1649  }
1650  if (data_type != entry_data_type) {
1651  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1652  }
1654  afw_value_get_data_type(value, xctx),
1655  i1, i2, xctx) != 0;
1656  if (!is_true) {
1657  break;
1658  }
1659  }
1660  break;
1661 
1662  case afw_query_criteria_filter_op_id_lt:
1663  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1664  for (is_true = false, iterator = NULL;;) {
1665  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1666  if (!i2) {
1667  break;
1668  }
1669  if (data_type != entry_data_type) {
1670  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1671  }
1673  afw_value_get_data_type(value, xctx),
1674  i1, i2, xctx) < 0;
1675  if (is_true) {
1676  break;
1677  }
1678  }
1679  break;
1680 
1681  case afw_query_criteria_filter_op_id_le:
1682  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1683  for (is_true = false, iterator = NULL;;) {
1684  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1685  if (!i2) {
1686  break;
1687  }
1688  if (data_type != entry_data_type) {
1689  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1690  }
1692  afw_value_get_data_type(value, xctx),
1693  i1, i2, xctx) <= 0;
1694  if (is_true) {
1695  break;
1696  }
1697  }
1698  break;
1699 
1700  case afw_query_criteria_filter_op_id_gt:
1701  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1702  for (is_true = false, iterator = NULL;;) {
1703  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1704  if (!i2) {
1705  break;
1706  }
1707  if (data_type != entry_data_type) {
1708  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1709  }
1711  afw_value_get_data_type(value, xctx),
1712  i1, i2, xctx) > 0;
1713  if (is_true) {
1714  break;
1715  }
1716  }
1717  break;
1718 
1719  case afw_query_criteria_filter_op_id_ge:
1720  i1 = (const void *)&((const afw_value_evaluated_t *)entry_value)->internal;
1721  for (is_true = false, iterator = NULL;;) {
1722  afw_list_get_next_internal(list, &iterator, &entry_data_type, &i2, xctx);
1723  if (!i2) {
1724  break;
1725  }
1726  if (data_type != entry_data_type) {
1727  AFW_THROW_ERROR_Z(general, "data type mismatch", xctx);
1728  }
1730  afw_value_get_data_type(value, xctx),
1731  i1, i2, xctx) >= 0;
1732  if (is_true) {
1733  break;
1734  }
1735  }
1736  break;
1737 
1738  case afw_query_criteria_filter_op_id_in:
1739  AFW_THROW_ERROR_Z(general, "Not implemented", xctx);
1768  case afw_query_criteria_filter_op_id_match:
1769  AFW_THROW_ERROR_Z(general, "Not implemented", xctx);
1790  case afw_query_criteria_filter_op_id_contains:
1791  AFW_THROW_ERROR_Z(general, "Not implemented", xctx);
1810  default:
1811  AFW_THROW_ERROR_Z(general,
1812  "Error occurred processing query filter", xctx);
1813 
1814  }
1815 
1816  return is_true;
1817 }
1818 
1819 
1820 /* ------------------------------------------------------------------------- */
1821 /* External function defines */
1822 /* ------------------------------------------------------------------------- */
1823 
1824 /*
1825  *
1826  * Parse query string.
1827  *
1828  * <query-string> ::= ( <sort> | <select> | <filter> ) ( '&' <query-string> )*
1829  * <sort> ::= 'sort(' ( '+' | '-' ) <name> ( ',' ( '+' | '-' ) <name> )* ')'
1830  * <select> ::= 'select(' <name> ( ',' <name> )* ')'
1831  * <filter> ::= <function> |
1832  * <term> # tree if string followed by '('
1833  * <function> ::= 'and' '(' <function> ( ',' <function> )* ')' |
1834  * 'or' '(' <function> ( ',' <function> )* ')' |
1835  * <operator> '(' <function> |
1836  * ( <name> ',' <literal> | <list> ) # depends on operator
1837  * ')'
1838  * <sugar> ::= <term> ( ( '|' | ',' ) <term> )*
1839  * <term> ::= <factor> ( ( '&' | ';' ) <factor> )*
1840  * <factor> ::= <relation> | ( <sugar> )
1841  * <relation> ::= ( <name> <op1> <literal> ) | ( <name> <op2> <list> )
1842  * <op1> ::= '=' | '==' | '=eq=' | '!=' | '=ne=' | '=lt=' | '=le=' |
1843  * '=lte=' | '=gt=' | '=ge=' | '=gte=' | '=match=' |
1844  * '=contains='
1845  * <op2> ::= '=in='
1846  * <list> ::= '(' <literal> ( ',' <literal> )* ')'
1847  *
1848  * The delimiters are "&=<>!()|,;+-# \0". There is a space before \0.
1849  *
1850  * In <query-string>: <sort>, <select> and <filter> can only occur once.
1851  *
1852  * In <list>, the comma separator can be % encoded. DStore seems to do this.
1853  * This may be an issue if <literal> should contain an encoded comma.
1854  *
1855  * <name> is a property name. Dot notation is allowed for referencing
1856  * properties of embedded objects.
1857  *s
1858  * <literal> is a URL encoded string. All delimiters in the string must be %
1859  * encoded.
1860  *
1861  */
1864  const afw_utf8_t *url_encoded_rql_string,
1865  const afw_object_type_t *object_type,
1866  const afw_pool_t *p, afw_xctx_t *xctx)
1867 {
1868  afw_query_criteria_t *criteria;
1869  impl_string_parser_t parser;
1870  const afw_utf8_z_t *queryz;
1873 
1874  /* Initialize parser buffer. */
1875  memset(&parser, 0, sizeof(parser));
1876  parser.xctx = xctx;
1877  parser.p = p;
1878 
1879  /* Allocate memory for criteria. */
1880  criteria = afw_pool_calloc_type(p, afw_query_criteria_t, xctx);
1881  criteria->object_type = object_type;
1882  parser.criteria = criteria;
1883 
1884  /* If there is no query string, return. */
1885  if (!url_encoded_rql_string ||
1886  url_encoded_rql_string->len == 0 ||
1887  *(url_encoded_rql_string->s) == '#')
1888  {
1889  return criteria;
1890  }
1891 
1892  /* Parse string. */
1893  queryz = afw_utf8_z_create(url_encoded_rql_string->s, url_encoded_rql_string->len, p, xctx);
1894  parser.criteria->origin_query_string = afw_utf8_create(queryz,
1895  url_encoded_rql_string->len, p, xctx);
1896  parser.start = parser.criteria->origin_query_string->s;
1897  parser.c = parser.start;
1898 
1899  if (*(parser.c) != 0) {
1900  while (1) {
1901 
1902  /* If "sort(", parser sort criteria. It can only occur once. */
1903  if (afw_utf8_z_starts_with_z(parser.c, "sort(")) {
1904  if (parser.criteria->first_sort) {
1905  AFW_THROW_ERROR_Z(general,
1906  "sort() specified multiple times in query string",
1907  xctx);
1908  }
1909  parser.c += strlen("sort(");
1910  parser.criteria->first_sort = impl_parse_string_sort(&parser);
1911  }
1912 
1913  /* If "select(", parser select criteria. It can only occur once. */
1914  else if (afw_utf8_z_starts_with_z(parser.c, "select(")) {
1915  if (parser.criteria->select) {
1916  AFW_THROW_ERROR_Z(general,
1917  "select() specified multiple times in query string",
1918  xctx);
1919  }
1920  parser.c += strlen("select(");
1921  parser.criteria->select = impl_parse_string_select(&parser);
1922  }
1923 
1924  /* If "limit(", parser limit criteria. It can only occur once.
1925  else if (afw_utf8_z_starts_with_z(parser.c, "select(")) {
1926  if (parser.criteria->limit) {
1927  AFW_THROW_ERROR_Z(general,
1928  "limit() specified multiple times in query string",
1929  xctx);
1930  }
1931  parser.c += strlen("limit(");
1932  impl_parse_string_limit(&parser);
1933  }
1934  */
1935 
1936  /* Anything else must be a filter. */
1937  else {
1938  if (parser.criteria->filter) {
1939  AFW_THROW_ERROR_Z(general,
1940  "Filter in query string is malformed", xctx);
1941  }
1942  impl_parse_string_filter(&parser,
1943  &filter,
1944  &tree,
1947  parser.criteria->filter = filter;
1948  parser.criteria->tree = tree;
1949  }
1950 
1951  /*
1952  * Get next token. If it is an '&", continue. Otherwise, anything
1953  * except the end of the query sting is an error.
1954  */
1955  impl_get_token(&parser);
1956  if (parser.token_type == impl_token_type_end) {
1957  break;
1958  }
1959  if ((parser.token_type != impl_token_type_delimiter) ||
1960  (*(parser.token.s) != '&'))
1961  {
1962  AFW_THROW_ERROR_Z(general,
1963  "query filter is malformed", xctx);
1964  }
1965  }
1966  }
1967 
1968  return criteria;
1969 }
1970 
1971 
1972 
1973 /* Parse URI encoded query string. */
1976  const afw_object_t *query_object,
1977  const afw_object_type_t *object_type,
1978  const afw_pool_t *p, afw_xctx_t *xctx)
1979 {
1980  afw_query_criteria_t *criteria;
1981  const afw_object_t *filter_object;
1982  const afw_iterator_t *iterator;
1983  const afw_list_t *sort;
1984  const afw_list_t *select;
1985  const afw_utf8_t *property_name;
1986  const afw_utf8_t *url_encoded_rql_string;
1987  const afw_value_t *value;
1989 
1990  /* If urlEncodedRQLString property present, process it and return result. */
1991  url_encoded_rql_string = afw_object_old_get_property_as_string(
1992  query_object, &afw_s_urlEncodedRQLString, xctx);
1993  if (url_encoded_rql_string) {
1994 
1995  /* Make sure only urlEncodedRQLString is specified. */
1996  for (iterator = NULL;;) {
1997  value = afw_object_get_next_property(query_object,
1998  &iterator, &property_name, xctx);
1999  if (!value) {
2000  break;
2001  }
2002  if (!afw_utf8_equal(property_name, &afw_s_urlEncodedRQLString)) {
2003  AFW_THROW_ERROR_Z(general,
2004  "Property urlEncodedRQLString must be specified alone",
2005  xctx);
2006  }
2007  }
2008 
2009  /* Parse urlEncodedRQLString and return criteria. */
2011  url_encoded_rql_string, object_type, p, xctx);
2012  }
2013 
2014  /* Otherwise, process based on filter, select, and sort properties. */
2015  criteria = afw_pool_calloc_type(p, afw_query_criteria_t, xctx);
2016  criteria->query_object = query_object;
2017  criteria->object_type = object_type;
2018  memset(&parser, 0, sizeof(parser));
2019  parser.xctx = xctx;
2020  parser.p = p;
2021  parser.criteria = criteria;
2022 
2023  for (
2024  iterator = NULL,
2025  sort = NULL,
2026  select = NULL,
2027  property_name = NULL;
2028  ;)
2029  {
2030  value = afw_object_get_next_property(query_object,
2031  &iterator, &property_name, xctx);
2032  if (!value) {
2033  break;
2034  }
2035 
2036  /* filter property */
2037  if (afw_utf8_equal(property_name, &afw_s_filter)) {
2038  filter_object = afw_value_as_object(value, xctx);
2039  impl_AdaptiveQueryCriteria_object_parse_filter(&parser,
2040  filter_object, &criteria->filter, &criteria->tree,
2042  }
2043 
2044  /* select property */
2045  else if (afw_utf8_equal(property_name, &afw_s_select)) {
2046  select = afw_value_as_list(value, xctx);
2047  criteria->select = impl_AdaptiveQueryCriteria_object_parse_select(
2048  &parser, select);
2049  }
2050 
2051  /* sort property */
2052  else if (afw_utf8_equal(property_name, &afw_s_sort)) {
2053  sort = afw_value_as_list(value, xctx);
2054  criteria->first_sort = impl_AdaptiveQueryCriteria_object_parse_sort(
2055  &parser, sort);
2056  }
2057 
2058  /* unknown property */
2059  else {
2060  AFW_THROW_ERROR_FZ(general, xctx,
2061  "Unknown query criteria property %" AFW_UTF8_FMT,
2062  AFW_UTF8_FMT_ARG(property_name));
2063  }
2064  }
2065 
2066  return criteria;
2067 }
2068 
2069 
2070 /* Test object against query criteria. */
2073  const afw_object_t *obj,
2074  const afw_query_criteria_t *criteria,
2075  const afw_pool_t *p,
2076  afw_xctx_t *xctx)
2077 {
2078  const afw_query_criteria_filter_entry_t *entry;
2079  const afw_value_t *value;
2080  afw_boolean_t is_true;
2081 
2082  /* Check object against filter. No filter always results in true. */
2083  entry = (criteria) ? criteria->filter : NULL;
2084  is_true = true;
2085  while (entry) {
2087  entry->property_name, xctx);
2088 
2089  /* If property does not exists, always fail. */
2090  if (!value) {
2091  is_true = false;
2092  break;
2093  }
2094 
2095  /* Make sure value is already evaluated. */
2096  if (!afw_value_is_defined_and_evaluated(value)) {
2097  AFW_THROW_ERROR_Z(general, "Expecting evaluated value", xctx);
2098  }
2099 
2100  /* Object values are not supported yet or ever. */
2101  if (afw_value_is_object(value) || afw_value_is_object(entry->value))
2102  {
2103  AFW_THROW_ERROR_Z(general,
2104  "Object values are not supported in query string",
2105  xctx);
2106  }
2107 
2108  /* Set on_true/on_value based on comparison. */
2109  is_true = impl_compare_value(entry, value, p, xctx);
2110  if (is_true) {
2111  if (entry->on_true == AFW_QUERY_CRITERIA_TRUE) {
2112  break;
2113  }
2114  if (entry->on_true == AFW_QUERY_CRITERIA_FALSE){
2115  is_true = false;
2116  break;
2117  }
2118  entry = entry->on_true;
2119  }
2120  else {
2121  if (entry->on_false == AFW_QUERY_CRITERIA_TRUE) {
2122  is_true = true;
2123  break;
2124  }
2125  if (entry->on_false == AFW_QUERY_CRITERIA_FALSE){
2126  break;
2127  }
2128  entry = entry->on_false;
2129  }
2130 
2131  }
2132 
2133  /* Return whether object passes filter. */
2134  return is_true;
2135 }
2136 
2137 
2138 
2139 /* ------------------------------------------------------------------------- */
2140 /* afw_query_criteria_to_AdaptiveQueryCriteria_object() */
2141 /* ------------------------------------------------------------------------- */
2142 
2143 
2144 static const afw_object_t *
2145 impl_criteria_filter_to_property_value(
2146  const afw_query_criteria_filter_entry_t *entry,
2147  const afw_pool_t *p, afw_xctx_t *xctx)
2148 {
2149  const afw_object_t *filter;
2151  const afw_list_t *filters;
2152  const afw_object_t *o;
2153  const afw_value_t *v;
2154 
2155  if (!entry)
2156  {
2157  return NULL;
2158  }
2159 
2160  filter = afw_object_create(p, xctx);
2161  afw_object_set_property_as_string(filter, &afw_s_op, entry->op_name, xctx);
2162 
2163  if (entry->op_id == afw_query_criteria_filter_op_id_and ||
2164  entry->op_id == afw_query_criteria_filter_op_id_or)
2165  {
2166  filters = afw_list_create_generic(p, xctx);
2167  afw_object_set_property_as_list(filter, &afw_s_filters, filters, xctx);
2168  for (e = entry->first_conjunctive_child; e; e = e->next_conjunctive_sibling) {
2169  o = impl_criteria_filter_to_property_value(e, p, xctx);
2170  v = afw_value_create_object(o, p, xctx);
2171  afw_list_add_value(filters, v, xctx);
2172  }
2173  }
2174 
2175  else {
2177  &afw_s_property, entry->property_name, xctx);
2178  afw_object_set_property(filter, &afw_s_value, entry->value, xctx);
2179  }
2180 
2181  return filter;
2182 }
2183 
2184 
2185 static const afw_list_t *
2186 impl_criteria_select_to_property_value(
2187  const afw_utf8_t * const *select,
2188  const afw_pool_t *p,
2189  afw_xctx_t *xctx)
2190 {
2191  const afw_list_t *result;
2192  const afw_utf8_t * const *e;
2193  afw_value_string_t *v;
2194 
2195  if (!select) {
2196  return NULL;
2197  }
2198 
2199  result = afw_list_create_generic(p, xctx);
2200 
2201  for (e = select; *e; e++) {
2202  v = afw_value_allocate_string(p, xctx);
2203  afw_memory_copy(&v->internal, *e);
2204  afw_list_add_value(result, (const afw_value_t *)v, xctx);
2205  }
2206 
2207  return result;
2208 }
2209 
2210 
2211 static const afw_list_t *
2212 impl_criteria_sort_to_property_value(
2213  const afw_query_criteria_sort_entry_t *sort_entry,
2214  const afw_pool_t *p,
2215  afw_xctx_t *xctx)
2216 {
2217  const afw_list_t *result;
2219  afw_value_string_t *v;
2220  afw_utf8_octet_t *c;
2221 
2222  if (!sort_entry) {
2223  return NULL;
2224  }
2225 
2226  result = afw_list_create_generic(p, xctx);
2227 
2228  for (e = sort_entry; e; e = e->next) {
2229  v = afw_value_allocate_string(p, xctx);
2230  v->internal.len = e->property_name->len + 1;
2231  v->internal.s = c = afw_pool_malloc(p, v->internal.len, xctx);
2232  *c++ = (e->descending) ? '-' : '+';
2233  memcpy(c, e->property_name->s, e->property_name->len);
2234  afw_list_add_value(result, (const afw_value_t *)v, xctx);
2235  }
2236 
2237  return result;
2238 }
2239 
2240 
2241 
2242 /* Convert query criteria to a _AdaptiveQueryCriteria_ object. */
2243 AFW_DEFINE(const afw_object_t *)
2245  const afw_query_criteria_t *criteria,
2246  const afw_pool_t *p,
2247  afw_xctx_t *xctx)
2248 {
2249  const afw_object_t *object;
2250  const afw_object_t *filter;
2251  const afw_list_t *select;
2252  const afw_list_t *sort;
2253 
2254  /* Create object for result. */
2255  object = afw_object_create_managed(p, xctx);
2256  afw_object_meta_set_object_type_id(object, &afw_s__AdaptiveQueryCriteria_,
2257  xctx);
2258 
2259  /* If no criteria, return empty object. */
2260  if (!criteria) {
2261  return object;
2262  }
2263 
2264  /* filter */
2265  filter = impl_criteria_filter_to_property_value(
2266  criteria->tree, object->p, xctx);
2267  if (filter) {
2269  &afw_s_filter, filter, xctx);
2270  }
2271 
2272  /* select */
2273  if (criteria->select) {
2274  select = impl_criteria_select_to_property_value(
2275  criteria->select, object->p, xctx);
2277  &afw_s_select, select, xctx);
2278  }
2279 
2280  /* sort */
2281  if (criteria->first_sort) {
2282  sort = impl_criteria_sort_to_property_value(
2283  criteria->first_sort, object->p, xctx);
2285  &afw_s_sort, sort, xctx);
2286  }
2287 
2288  /* Return _AdaptiveQueryCriteria_ object */
2289  return object;
2290 }
2291 
2292 
2293 
2294 /* ------------------------------------------------------------------------- */
2295 /* afw_query_criteria_to_query_string() */
2296 /* ------------------------------------------------------------------------- */
2297 
2298 static void
2299 impl_entry_to_query_string(
2300  const afw_writer_t *w,
2301  const afw_query_criteria_filter_entry_t *entry,
2302  afw_boolean_t style_function,
2303  afw_boolean_t style_long,
2304  afw_boolean_t style_semi_colon_comma,
2305  afw_boolean_t need_ampersand,
2306  afw_boolean_t inner_conjunction,
2307  const afw_pool_t *p,
2308  afw_xctx_t *xctx)
2309 {
2310  const afw_iterator_t *iterator;
2311  const afw_utf8_t *property_name;
2312  const afw_value_t *value;
2313  afw_boolean_t first_loop;
2314  const afw_utf8_z_t *fiql_op;
2315  const afw_utf8_t *s;
2316  const impl_rql_op_t *rql_op;
2317  const afw_list_t *list;
2318  const afw_query_criteria_filter_entry_t *child;
2319  afw_boolean_t first_time;
2320  afw_boolean_t need_close_parenthesis;
2321 
2322  /* Conjunction */
2323  if (afw_utf8_equal(entry->op_name, &afw_s_and) ||
2324  afw_utf8_equal(entry->op_name, &afw_s_or))
2325  {
2326  if (style_function) {
2327  for (child = entry->first_conjunctive_child, first_loop = true;
2328  child;
2329  child = child->next_conjunctive_sibling)
2330  {
2331  if (first_loop) {
2332  if (need_ampersand) {
2333  afw_writer_write_z(w, "&", xctx);
2334  }
2335  first_loop = false;
2336  afw_writer_write_utf8(w, entry->op_name, xctx);
2337  afw_writer_write_z(w, "(", xctx);
2338  }
2339  else {
2340  afw_writer_write_z(w, ",", xctx);
2341  }
2342  impl_entry_to_query_string(w, child,
2343  style_function, style_long,
2344  style_semi_colon_comma, false, true , p, xctx);
2345  }
2346 
2347  afw_writer_write_z(w, ")", xctx);
2348  }
2349 
2350  else {
2351  need_close_parenthesis = false;
2352  fiql_op = afw_utf8_equal(entry->op_name, &afw_s_and)
2353  ? (style_semi_colon_comma ? ";" : "&")
2354  : (style_semi_colon_comma ? "," : "|");
2355  for (child = entry->first_conjunctive_child, first_loop = true;
2356  child;
2357  child = child->next_conjunctive_sibling)
2358  {
2359 
2360  if (first_loop) {
2361  if (need_ampersand) {
2362  afw_writer_write_z(w, "&", xctx);
2363  }
2364  if (inner_conjunction ||
2365  (!inner_conjunction &&
2366  afw_utf8_equal(entry->op_name, &afw_s_or)))
2367  {
2368  afw_writer_write_z(w, "(", xctx);
2369  need_close_parenthesis = true;
2370  }
2371  }
2372  else {
2373  afw_writer_write_z(w, fiql_op, xctx);
2374  }
2375 
2376  impl_entry_to_query_string(w, child,
2377  style_function, style_long,
2378  style_semi_colon_comma, false, true, p, xctx);
2379 
2380  first_loop = false;
2381  }
2382  if (need_close_parenthesis) {
2383  afw_writer_write_z(w, ")", xctx);
2384  }
2385  }
2386  }
2387 
2388  /* Comparision */
2389  else {
2390  if (need_ampersand) {
2391  afw_writer_write_z(w, "&", xctx);
2392  }
2393 
2394  rql_op = impl_find_rql_op(entry->op_name);
2395  if (!rql_op || !rql_op->can_be_property) {
2396  AFW_THROW_ERROR_FZ(general, xctx,
2397  "Invalid op name %" AFW_UTF8_FMT,
2398  AFW_UTF8_FMT_ARG(entry->op_name));
2399  }
2400  property_name = afw_uri_encode(entry->property_name,
2401  AFW_URI_OCTET_UNRESERVED, p, xctx);
2402 
2403  if (style_function) {
2404  afw_writer_write_utf8(w, entry->op_name, xctx);
2405  afw_writer_write_z(w, "(", xctx);
2406  afw_writer_write_utf8(w, property_name, xctx);
2407  if (rql_op->op->is_list) {
2408  list = afw_value_as_list(entry->value, xctx);
2409  for (iterator = NULL, first_time = true;;) {
2410  value = afw_list_get_next_value(list, &iterator, p, xctx);
2411  if (!value) {
2412  break;
2413  }
2414  afw_writer_write_z(w, ",", xctx);
2415  s = afw_value_as_utf8(value, p, xctx);
2416  s = afw_uri_encode(s,
2418  afw_writer_write_utf8(w, s, xctx);
2419  }
2420  }
2421  else {
2422  afw_writer_write_z(w, ",", xctx);
2423  s = afw_value_as_utf8(entry->value, p, xctx);
2424  s = afw_uri_encode(s,
2426  afw_writer_write_utf8(w, s, xctx);
2427  }
2428  afw_writer_write_z(w, ")", xctx);
2429  }
2430 
2431  else {
2432  afw_writer_write_utf8(w, property_name, xctx);
2433  if (style_long) {
2434  if (afw_utf8_equal(entry->op_name, &afw_s_ge)) {
2435  afw_writer_write_z(w, "=gte=", xctx);
2436  }
2437  else if (afw_utf8_equal(entry->op_name, &afw_s_le)) {
2438  afw_writer_write_z(w, "=lte=", xctx);
2439  }
2440  else {
2441  afw_writer_write_z(w, "=", xctx);
2442  afw_writer_write_utf8(w, entry->op_name, xctx);
2443  afw_writer_write_z(w, "=", xctx);
2444  }
2445  }
2446  else {
2447  if (afw_utf8_equal(entry->op_name, &afw_s_ne)) {
2448  afw_writer_write_z(w, "!=", xctx);
2449  }
2450  else {
2451  afw_writer_write_z(w, "=", xctx);
2452  if (!afw_utf8_equal(entry->op_name, &afw_s_eq)) {
2453  afw_writer_write_utf8(w, entry->op_name, xctx);
2454  afw_writer_write_z(w, "=", xctx);
2455  }
2456  }
2457  }
2458 
2459  if (rql_op->op->is_list) {
2460  list = afw_value_as_list(entry->value, xctx);
2461  for (iterator = NULL, first_time = true;;) {
2462  value = afw_list_get_next_value(list, &iterator, p, xctx);
2463  if (!value) {
2464  break;
2465  }
2466  if (first_time) {
2467  first_time = false;
2468  afw_writer_write_z(w, "(", xctx);
2469  }
2470  else {
2471  afw_writer_write_z(w, ",", xctx);
2472  }
2473  s = afw_value_as_utf8(value, p, xctx);
2474  s = afw_uri_encode(s,
2476  afw_writer_write_utf8(w, s, xctx);
2477  afw_writer_write_z(w, ")", xctx);
2478  }
2479  }
2480  else {
2481  s = afw_value_as_utf8(entry->value, p, xctx);
2482  s = afw_uri_encode(s,
2484  afw_writer_write_utf8(w, s, xctx);
2485  }
2486  }
2487  }
2488 }
2489 
2490 /* Convert query criteria to a url encoded rql string. */
2491 AFW_DEFINE(const afw_utf8_t *)
2493  const afw_query_criteria_t *criteria,
2495  const afw_pool_t *p,
2496  afw_xctx_t *xctx)
2497 {
2498  const afw_writer_t *w;
2499  afw_utf8_t ws;
2500  const afw_utf8_t *result;
2501  const afw_utf8_t *s;
2502  afw_boolean_t needs_ampersand, first_inner;
2503  const afw_utf8_t * const *sa;
2505  afw_boolean_t style_function;
2506  afw_boolean_t style_long;
2507  afw_boolean_t style_semi_colon_comma;
2508 
2509  /* Return empty string if no query_object. */
2510  if (!criteria) {
2511  return &afw_s_a_empty_string;
2512  }
2513 
2514  /* Create writer for result. */
2515  needs_ampersand = false;
2516  w = afw_utf8_writer_create(NULL, p, xctx);
2517 
2518  /* select */
2519  if (criteria->select) {
2520  for (sa = criteria->select, first_inner = true; *sa; sa++)
2521  {
2522  if (first_inner) {
2523  first_inner = false;
2524  afw_writer_write_z(w, "select(", xctx);
2525  needs_ampersand = true;
2526  }
2527  else {
2528  afw_writer_write_z(w, ",", xctx);
2529  }
2530  s = afw_uri_encode(*sa,
2531  AFW_URI_OCTET_UNRESERVED, p, xctx);
2532  afw_writer_write_utf8(w, s, xctx);
2533  }
2534  if (!first_inner) {
2535  afw_writer_write_z(w, ")", xctx);
2536  }
2537  }
2538 
2539  /* sort */
2540  for (se = criteria->first_sort, first_inner = true; se; se = se->next)
2541  {
2542  if (first_inner) {
2543  first_inner = false;
2544  if (needs_ampersand) {
2545  afw_writer_write_z(w, "&", xctx);
2546  }
2547  afw_writer_write_z(w, "sort(", xctx);
2548  needs_ampersand = true;
2549  }
2550  else {
2551  afw_writer_write_z(w, ",", xctx);
2552  }
2553 
2554  if (se->descending) {
2555  afw_writer_write_z(w, "-", xctx);
2556  }
2557  else {
2558  afw_writer_write_z(w, "%2b", xctx);
2559  }
2560  s = afw_uri_encode(se->property_name,
2561  AFW_URI_OCTET_UNRESERVED, p, xctx);
2562  afw_writer_write_utf8(w, s, xctx);
2563  }
2564  if (!first_inner) {
2565  afw_writer_write_z(w, ")", xctx);
2566  }
2567 
2568 
2569  /* filter. */
2570  if (criteria->filter) {
2571  style_function = false;
2572  style_long = false;
2573  style_semi_colon_comma = false;
2574  switch (style) {
2575 
2576  case afw_query_criteria_style_operator:
2577  break;
2578 
2579  case afw_query_criteria_style_operator_long:
2580  style_long = true;
2581  break;
2582 
2583  case afw_query_criteria_style_semicolon_comma:
2584  style_semi_colon_comma = true;
2585  break;
2586 
2587  case afw_query_criteria_style_function:
2588  style_function = true;
2589  break;
2590 
2591  default:
2592  AFW_THROW_ERROR_Z(general, "invalid value for style parameter", xctx);
2593  }
2594 
2595  impl_entry_to_query_string(w, criteria->tree,
2596  style_function, style_long, style_semi_colon_comma,
2597  needs_ampersand, false, p, xctx);
2598  }
2599 
2600  /* Return RQL */
2601  afw_utf8_writer_current_string(w, &ws, xctx);
2602  result = afw_utf8_clone(&ws, p, xctx);
2603  afw_writer_release(w, xctx);
2604  return result;
2605 }
AFW_DEFINE(const afw_object_t *)
Adaptive Framework Core Internal.
afw_value_create_list(const afw_list_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type list value.
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
#define afw_object_old_get_property_as_list(object, property_name, xctx)
Get property function for data type list value.
afw_value_as_list(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type list.
afw_object_set_property_as_list(const afw_object_t *object, const afw_utf8_t *property_name, const afw_list_t *internal, afw_xctx_t *xctx)
Set property function for data type list values.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
afw_object_set_property_as_object(const afw_object_t *object, const afw_utf8_t *property_name, const afw_object_t *internal, afw_xctx_t *xctx)
Set property function for data type object values.
afw_value_create_object(const afw_object_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type object value.
afw_value_as_object(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type object.
afw_value_create_string(const afw_utf8_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type string value.
#define afw_object_old_get_property_as_string(object, property_name, xctx)
Get property function for data type string value.
afw_value_allocate_string(const afw_pool_t *p, afw_xctx_t *xctx)
Allocate function for unmanaged data type string value.
afw_data_type_string
Data type struct for string.
afw_object_set_property_as_string(const afw_object_t *object, const afw_utf8_t *property_name, const afw_utf8_t *internal, afw_xctx_t *xctx)
Set property function for data type string values.
afw_value_as_string(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type string.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
struct afw_iterator_s afw_iterator_t
_Bool afw_boolean_t
Definition: afw_common.h:373
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
struct afw_const_utf8_a_stack_s afw_const_utf8_a_stack_t
#define AFW_SIZE_T_FMT
Format string specifier used for afw_size_t.
Definition: afw_common.h:341
#define afw_data_type_compare_internal(instance, internal1, internal2, xctx)
Call method compare_internal of interface afw_data_type.
#define AFW_CATCH_UNHANDLED
Catch an unhandled error that occurs in a AFW_TRY block.
Definition: afw_error.h:684
#define AFW_ENDTRY
Ends an AFW try block.
Definition: afw_error.h:727
#define AFW_TRY
Begin an AFW TRY block.
Definition: afw_error.h:634
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
#define afw_list_get_next_internal(instance, iterator, data_type, internal, xctx)
Call method get_next_internal of interface afw_list.
#define afw_list_get_next_value(instance, iterator, p, xctx)
Call method get_next_value of interface afw_list.
#define afw_list_get_data_type(instance, xctx)
Call method get_data_type of interface afw_list.
#define AFW_LIST_INITIALIZE_WRAPPER_FOR_ARRAY(instance, _internal, _indirect, _data_type, _count)
Helper macro to fill out afw_list_wrapper_for_array_self_t.
Definition: afw_list.h:276
afw_list_create_wrapper_for_array(const void *array, afw_boolean_t indirect, const afw_data_type_t *data_type, afw_size_t count, const afw_pool_t *p, afw_xctx_t *xctx)
Create a immutable list wrapper for an array.
#define afw_list_create_generic(p, xctx)
Create an value list in memory.
Definition: afw_list.h:81
afw_list_add_value(const afw_list_t *instance, const afw_value_t *value, afw_xctx_t *xctx)
Call method add_value of interface afw_list_setter.
Definition: afw_list.c:104
#define afw_memory_copy(to, from)
Copy to preallocated memory of same type.
Definition: afw_memory.h:39
#define afw_object_get_property(instance, property_name, xctx)
Call method get_property of interface afw_object.
#define afw_object_get_next_property(instance, iterator, property_name, xctx)
Call method get_next_property of interface afw_object.
afw_object_meta_set_object_type_id(const afw_object_t *instance, const afw_utf8_t *object_type_id, afw_xctx_t *xctx)
Set object's object type id.
afw_object_type_property_type_get(const afw_object_type_t *object_type, const afw_utf8_t *property_name, afw_xctx_t *xctx)
Get property type object for property.
afw_object_get_property_extended(const afw_object_t *instance, const afw_utf8_t *property_name_extended, afw_xctx_t *xctx)
Get the value of an object's own property or embedded property.
Definition: afw_object.c:274
#define afw_object_create_managed(p, xctx)
Create an empty entity object in its own pool.
Definition: afw_object.h:913
#define afw_object_create(p, xctx)
Create an empty unmanaged object in memory.
Definition: afw_object.h:948
afw_object_set_property(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_value_t *value, afw_xctx_t *xctx)
Set the value of an object's property.
Definition: afw_object.c:46
#define afw_pool_malloc(instance, size, xctx)
Call method malloc of interface afw_pool.
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
afw_query_criteria_test_object(const afw_object_t *obj, const afw_query_criteria_t *criteria, const afw_pool_t *p, afw_xctx_t *xctx)
Test object against query criteria.
#define AFW_QUERY_CRITERIA_FALSE
afw_query_criteria_to_query_string(const afw_query_criteria_t *criteria, afw_query_criteria_style_t style, const afw_pool_t *p, afw_xctx_t *xctx)
Convert query criteria to query string.
afw_query_criteria_filter_op_id_t
afw_query_criteria_parse_AdaptiveQueryCriteria_object(const afw_object_t *query_object, const afw_object_type_t *object_type, const afw_pool_t *p, afw_xctx_t *xctx)
Parse URI encoded query string.
afw_query_criteria_style_t
Query string style.
afw_query_criteria_to_AdaptiveQueryCriteria_object(const afw_query_criteria_t *criteria, const afw_pool_t *p, afw_xctx_t *xctx)
Convert query criteria to a AdaptiveQueryCriteria object.
afw_query_criteria_parse_url_encoded_rql_string(const afw_utf8_t *url_encoded_rql_string, const afw_object_type_t *object_type, const afw_pool_t *p, afw_xctx_t *xctx)
Parse URI encoded RQL query string.
#define AFW_QUERY_CRITERIA_TRUE
#define afw_stack_copy_and_release(instance, count, ptr, p, xctx)
Copy and release stack.
Definition: afw_stack.h:131
#define afw_stack_create(typedef_name, initial_count, maximum_count, create_subpool_pool, p, xctx)
Create a stack for the specified typedef.
Definition: afw_stack.h:40
#define afw_stack_push(instance, xctx)
Increment stack->top to location of next entry and returns *top.
Definition: afw_stack.h:195
#define AFW_URI_OCTET_ENCODE_COMPONENT_VALUE
encode except A-Z a-z 0-9 - _ . ! ~ * '
Definition: afw_uri.h:54
afw_uri_decode_create(const afw_utf8_octet_t *s, afw_size_t len, const afw_pool_t *p, afw_xctx_t *xctx)
Create a URI decoded string.
Definition: afw_uri.c:1023
afw_uri_encode(const afw_utf8_t *string, afw_uri_octet_type_t mask, const afw_pool_t *p, afw_xctx_t *xctx)
URI encode a string.
Definition: afw_uri.c:814
const afw_utf8_t * afw_utf8_concat(const afw_pool_t *p, afw_xctx_t *xctx,...)
Concatenate strings with result in specifed pool.
afw_boolean_t afw_utf8_equal_utf8_z(const afw_utf8_t *s1, const afw_utf8_z_t *s2_z)
Check to see if a string equals a utf8_z string.
void afw_utf8_writer_current_string(const afw_writer_t *writer, afw_utf8_t *current_string, afw_xctx_t *xctx)
Get the current string in a UTF-8 writer.
afw_utf8_z_printf_v(const afw_utf8_z_t *format_z, va_list ap, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_utf8.c:844
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
afw_boolean_t afw_utf8_z_starts_with_z(const afw_utf8_z_t *s1_z, const afw_utf8_z_t *s2_z)
Returns true if zero terminated s1 starts with zero terminated string s2.
Definition: afw_utf8.h:756
afw_utf8_z_create(const afw_utf8_octet_t *s, afw_size_t len, const afw_pool_t *p, afw_xctx_t *xctx)
Create a NFC Normalized zero terminated UTF-8 string in specified pool.
Definition: afw_utf8.c:366
const afw_writer_t * afw_utf8_writer_create(const afw_utf8_t *tab, const afw_pool_t *p, afw_xctx_t *xctx)
Create UTF-8 writer.
const afw_utf8_t * afw_utf8_clone(const afw_utf8_t *string, const afw_pool_t *p, afw_xctx_t *xctx)
Clone a utf-8 string into a specific pool.
Definition: afw_utf8.h:347
const afw_utf8_z_t * afw_utf8_to_utf8_z(const afw_utf8_t *string, const afw_pool_t *p, afw_xctx_t *xctx)
Convert utf8 to utf8_z in specified pool.
Definition: afw_utf8.h:529
#define afw_utf8_create(s, len, p, xctx)
Create utf-8 string without copy unless necessary in pool specified.
Definition: afw_utf8.h:239
#define afw_value_get_data_type(instance, xctx)
Call method get_data_type of interface afw_value.
afw_value_as_utf8(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_value.c:456
#define afw_value_is_defined_and_evaluated(A_VALUE)
Macro to determine if value is defined and evaluated.
Definition: afw_value.h:481
afw_value_convert(const afw_value_t *value, const afw_data_type_t *to_data_type, afw_boolean_t required, const afw_pool_t *p, afw_xctx_t *xctx)
Convert a value to a value/data type.
Definition: afw_value.c:584
#define afw_writer_release(instance, xctx)
Call method release of interface afw_writer.
#define afw_writer_write_z(writer, s_z, xctx)
Call afw_writer_write() with zero terminated string.
Definition: afw_writer.h:35
#define afw_writer_write_utf8(writer, S, xctx)
Call afw_writer_write() with a afw_utf8_t string.
Definition: afw_writer.h:45
Interface afw_data_type public struct.
Interface afw_list public struct.
Self for immutable list wrapper for a array.
Definition: afw_list.h:237
Interface afw_object public struct.
Struct for afw_object_type_t.
Interface afw_pool public struct.
Parsed filter entry from query string.
Parsed query criteria.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
Struct to access internal of all evaluated values.
Definition: afw_value.h:60
struct for data type list values.
Interface afw_value public struct.
struct for data type string values.
Interface afw_writer public struct.
Interface afw_xctx public struct.