mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
select: add support for selection to match string list subset, recognize { } operator
Using "[ ]" operator together with "&&" (or ",") inside causes the string list to be matched if and only if all the items given match the value reported and the number of items also match. This is strict list matching and the original behaviour we already have. In contrast to that, the new "{ }" operator together with "&&" inside causes the string list to be matched if and only if all the items given match the value reported but the number of items don't need to match. So we can provide a subset in selection criteria and if the subset is found, it matches. For example: $ lvs -o name,tags LV LV Tags lvol0 a lvol1 a,b lvol2 b,c,x lvol3 a,b,y $ lvs -o name,tags -S 'tags=[a,b]' LV LV Tags lvol1 a,b $ lvs -o name,tags -S 'tags={a,b}' LV LV Tags lvol1 a,b lvol3 a,b,y So in the example above the a,b is subset of a,b,y and therefore it also matches. Clearly, when using "||" (or "#") inside, the { } and [ ] is the same: $ lvs -o name,tags -S 'tags=[a#b]' LV LV Tags lvol0 a lvol1 a,b lvol2 b,c,x lvol3 a,b,y $ lvs -o name,tags -S 'tags={a#b}' LV LV Tags lvol0 a lvol1 a,b lvol2 b,c,x lvol3 a,b,y Also in addition to the above feature, fix list with single value matching when using [ ]: Before this patch: $ lvs -o name,tags -S 'tags=[a]' LV LV Tags lvol0 a lvol1 a,b lvol3 a,b,y With this patch applied: $ lvs -o name,tags -S 'tags=[a]' LV LV Tags lvol0 a In case neither [] or {} is used, assume {} (the behaviour is not changed here): $ lvs -o name,tags -S 'tags=a' LV LV Tags lvol0 a lvol1 a,b lvol3 a,b,y So in new terms 'tags=a' is equal to 'tags={a}'.
This commit is contained in:
parent
6dd98c1fa8
commit
fa793bed64
@ -1,5 +1,7 @@
|
|||||||
Version 1.02.89 -
|
Version 1.02.89 -
|
||||||
=================================
|
=================================
|
||||||
|
Add support for selection to match string list subset, recognize { } operator.
|
||||||
|
Fix string list selection with '[value]' to not match list that's superset.
|
||||||
Fix string list selection to match whole words only, not prefixes.
|
Fix string list selection to match whole words only, not prefixes.
|
||||||
|
|
||||||
Version 1.02.88 - 5th August 2014
|
Version 1.02.88 - 5th August 2014
|
||||||
|
@ -131,6 +131,8 @@ static struct op_def _op_cmp[] = {
|
|||||||
#define SEL_LIST_MASK 0x000F0000
|
#define SEL_LIST_MASK 0x000F0000
|
||||||
#define SEL_LIST_LS 0x00010000
|
#define SEL_LIST_LS 0x00010000
|
||||||
#define SEL_LIST_LE 0x00020000
|
#define SEL_LIST_LE 0x00020000
|
||||||
|
#define SEL_LIST_SUBSET_LS 0x00040000
|
||||||
|
#define SEL_LIST_SUBSET_LE 0x00080000
|
||||||
|
|
||||||
static struct op_def _op_log[] = {
|
static struct op_def _op_log[] = {
|
||||||
{ "&&", SEL_AND, "All fields must match" },
|
{ "&&", SEL_AND, "All fields must match" },
|
||||||
@ -142,6 +144,8 @@ static struct op_def _op_log[] = {
|
|||||||
{ ")", SEL_PRECEDENCE_PE, "Right parenthesis" },
|
{ ")", SEL_PRECEDENCE_PE, "Right parenthesis" },
|
||||||
{ "[", SEL_LIST_LS, "List start" },
|
{ "[", SEL_LIST_LS, "List start" },
|
||||||
{ "]", SEL_LIST_LE, "List end"},
|
{ "]", SEL_LIST_LE, "List end"},
|
||||||
|
{ "{", SEL_LIST_SUBSET_LS, "List subset start"},
|
||||||
|
{ "}", SEL_LIST_SUBSET_LE, "List subset end"},
|
||||||
{ NULL, 0, NULL},
|
{ NULL, 0, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1247,8 +1251,8 @@ static int _cmp_field_string(const char *field_id, const char *a, const char *b,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Matches if all items from selection string list match. */
|
/* Matches if all items from selection string list match list value strictly 1:1. */
|
||||||
static int _cmp_field_string_list_all(const struct str_list_sort_value *val,
|
static int _cmp_field_string_list_strict_all(const struct str_list_sort_value *val,
|
||||||
const struct selection_str_list *sel)
|
const struct selection_str_list *sel)
|
||||||
{
|
{
|
||||||
struct dm_str_list *sel_item;
|
struct dm_str_list *sel_item;
|
||||||
@ -1269,7 +1273,36 @@ static int _cmp_field_string_list_all(const struct str_list_sort_value *val,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Matches if any item from selection string list matches. */
|
/* Matches if all items from selection string list match a subset of list value. */
|
||||||
|
static int _cmp_field_string_list_subset_all(const struct str_list_sort_value *val,
|
||||||
|
const struct selection_str_list *sel)
|
||||||
|
{
|
||||||
|
struct dm_str_list *sel_item;
|
||||||
|
unsigned int i = 1, last_found = 1;;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
/* if value has no items and selection has at leas one, it's clear there's no match */
|
||||||
|
if ((val->items[0].len == 0) && dm_list_size(sel->list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Check selection is a subset of the value. */
|
||||||
|
dm_list_iterate_items(sel_item, sel->list) {
|
||||||
|
r = 0;
|
||||||
|
for (i = last_found; i <= val->items[0].len; i++) {
|
||||||
|
if ((strlen(sel_item->str) == val->items[i].len) &&
|
||||||
|
!strncmp(sel_item->str, val->value + val->items[i].pos, val->items[i].len)) {
|
||||||
|
last_found = i;
|
||||||
|
r = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!r)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matches if any item from selection string list matches list value. */
|
||||||
static int _cmp_field_string_list_any(const struct str_list_sort_value *val,
|
static int _cmp_field_string_list_any(const struct str_list_sort_value *val,
|
||||||
const struct selection_str_list *sel)
|
const struct selection_str_list *sel)
|
||||||
{
|
{
|
||||||
@ -1299,11 +1332,24 @@ static int _cmp_field_string_list(const char *field_id,
|
|||||||
const struct str_list_sort_value *value,
|
const struct str_list_sort_value *value,
|
||||||
const struct selection_str_list *selection, uint32_t flags)
|
const struct selection_str_list *selection, uint32_t flags)
|
||||||
{
|
{
|
||||||
int r;
|
int subset, r;
|
||||||
|
|
||||||
|
switch (selection->type & SEL_LIST_MASK) {
|
||||||
|
case SEL_LIST_LS:
|
||||||
|
subset = 0;
|
||||||
|
break;
|
||||||
|
case SEL_LIST_SUBSET_LS:
|
||||||
|
subset = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error(INTERNAL_ERROR "_cmp_field_string_list: unknown list type");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
switch (selection->type & SEL_MASK) {
|
switch (selection->type & SEL_MASK) {
|
||||||
case SEL_AND:
|
case SEL_AND:
|
||||||
r = _cmp_field_string_list_all(value, selection);
|
r = subset ? _cmp_field_string_list_subset_all(value, selection)
|
||||||
|
: _cmp_field_string_list_strict_all(value, selection);
|
||||||
break;
|
break;
|
||||||
case SEL_OR:
|
case SEL_OR:
|
||||||
r = _cmp_field_string_list_any(value, selection);
|
r = _cmp_field_string_list_any(value, selection);
|
||||||
@ -1932,11 +1978,11 @@ static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
|
|||||||
struct selection_str_list *ssl = NULL;
|
struct selection_str_list *ssl = NULL;
|
||||||
struct dm_str_list *item;
|
struct dm_str_list *item;
|
||||||
const char *begin_item, *end_item, *tmp;
|
const char *begin_item, *end_item, *tmp;
|
||||||
uint32_t end_op_flags, end_op_flag_hit = 0;
|
uint32_t op_flags, end_op_flag_expected, end_op_flag_hit = 0;
|
||||||
struct dm_str_list **arr;
|
struct dm_str_list **arr;
|
||||||
size_t list_size;
|
size_t list_size;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int list_end;
|
int list_end = 0;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
if (!(ssl = dm_pool_alloc(mem, sizeof(*ssl))) ||
|
if (!(ssl = dm_pool_alloc(mem, sizeof(*ssl))) ||
|
||||||
@ -1948,8 +1994,8 @@ static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
|
|||||||
ssl->type = 0;
|
ssl->type = 0;
|
||||||
*begin = s;
|
*begin = s;
|
||||||
|
|
||||||
if (!_tok_op_log(s, &tmp, SEL_LIST_LS)) {
|
if (!(op_flags = _tok_op_log(s, &tmp, SEL_LIST_LS | SEL_LIST_SUBSET_LS))) {
|
||||||
/* Only one item - SEL_LIST_LS and SEL_LIST_LE not used */
|
/* Only one item - SEL_LIST_{SUBSET_}LS and SEL_LIST_{SUBSET_}LE not used */
|
||||||
c = _get_and_skip_quote_char(&s);
|
c = _get_and_skip_quote_char(&s);
|
||||||
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) {
|
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) {
|
||||||
log_error(_str_list_item_parsing_failed, ft->id);
|
log_error(_str_list_item_parsing_failed, ft->id);
|
||||||
@ -1957,32 +2003,47 @@ static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
|
|||||||
}
|
}
|
||||||
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
|
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
|
||||||
goto_bad;
|
goto_bad;
|
||||||
ssl->type = SEL_OR;
|
ssl->type = SEL_OR | SEL_LIST_LS;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* More than one item - items enclosed in SEL_LIST_LS and SEL_LIST_LE.
|
/* More than one item - items enclosed in SEL_LIST_LS and SEL_LIST_LE
|
||||||
|
* or SEL_LIST_SUBSET_LS and SEL_LIST_SUBSET_LE.
|
||||||
* Each element is terminated by AND or OR operator or 'list end'.
|
* Each element is terminated by AND or OR operator or 'list end'.
|
||||||
* The first operator hit is then the one allowed for the whole list,
|
* The first operator hit is then the one allowed for the whole list,
|
||||||
* no mixing allowed!
|
* no mixing allowed!
|
||||||
*/
|
*/
|
||||||
end_op_flags = SEL_LIST_LE | SEL_AND | SEL_OR;
|
|
||||||
|
/* Are we using [] or {} for the list? */
|
||||||
|
end_op_flag_expected = (op_flags == SEL_LIST_LS) ? SEL_LIST_LE : SEL_LIST_SUBSET_LE;
|
||||||
|
|
||||||
|
op_flags = SEL_LIST_LE | SEL_LIST_SUBSET_LE | SEL_AND | SEL_OR;
|
||||||
s++;
|
s++;
|
||||||
while (*s) {
|
while (*s) {
|
||||||
s = _skip_space(s);
|
s = _skip_space(s);
|
||||||
c = _get_and_skip_quote_char(&s);
|
c = _get_and_skip_quote_char(&s);
|
||||||
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, end_op_flags, NULL))) {
|
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, op_flags, NULL))) {
|
||||||
log_error(_str_list_item_parsing_failed, ft->id);
|
log_error(_str_list_item_parsing_failed, ft->id);
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
s = _skip_space(s);
|
s = _skip_space(s);
|
||||||
|
|
||||||
if (!(end_op_flag_hit = _tok_op_log(s, &tmp, end_op_flags))) {
|
if (!(end_op_flag_hit = _tok_op_log(s, &tmp, op_flags))) {
|
||||||
log_error("Invalid operator in selection list.");
|
log_error("Invalid operator in selection list.");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_end = end_op_flag_hit == SEL_LIST_LE;
|
if (end_op_flag_hit & (SEL_LIST_LE | SEL_LIST_SUBSET_LE)) {
|
||||||
|
list_end = 1;
|
||||||
|
if (end_op_flag_hit != end_op_flag_expected) {
|
||||||
|
for (i = 0; _op_log[i].string; i++)
|
||||||
|
if (_op_log[i].flags == end_op_flag_expected)
|
||||||
|
break;
|
||||||
|
log_error("List ended with incorrect character, "
|
||||||
|
"expecting \'%s\'.", _op_log[i].string);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ssl->type) {
|
if (ssl->type) {
|
||||||
if (!list_end && !(ssl->type & end_op_flag_hit)) {
|
if (!list_end && !(ssl->type & end_op_flag_hit)) {
|
||||||
@ -1990,8 +2051,12 @@ static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
|
|||||||
"in selection list at a time.");
|
"in selection list at a time.");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
ssl->type = list_end ? SEL_OR : end_op_flag_hit;
|
if (list_end)
|
||||||
|
ssl->type = end_op_flag_expected == SEL_LIST_LE ? SEL_AND : SEL_OR;
|
||||||
|
else
|
||||||
|
ssl->type = end_op_flag_hit;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
|
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
|
||||||
goto_bad;
|
goto_bad;
|
||||||
@ -2002,11 +2067,17 @@ static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end_op_flag_hit != SEL_LIST_LE) {
|
if (!(end_op_flag_hit & (SEL_LIST_LE | SEL_LIST_SUBSET_LE))) {
|
||||||
log_error("Missing list end for selection field %s", ft->id);
|
log_error("Missing list end for selection field %s", ft->id);
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Store information whether [] or {} was used. */
|
||||||
|
if ((end_op_flag_expected == SEL_LIST_LE))
|
||||||
|
ssl->type |= SEL_LIST_LS;
|
||||||
|
else
|
||||||
|
ssl->type |= SEL_LIST_SUBSET_LS;
|
||||||
|
|
||||||
/* Sort the list. */
|
/* Sort the list. */
|
||||||
if (!(list_size = dm_list_size(ssl->list))) {
|
if (!(list_size = dm_list_size(ssl->list))) {
|
||||||
log_error(INTERNAL_ERROR "_tok_value_string_list: list has no items");
|
log_error(INTERNAL_ERROR "_tok_value_string_list: list has no items");
|
||||||
|
Loading…
Reference in New Issue
Block a user