Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 16:36:00 +0300
committerAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 16:36:00 +0300
commite222c02f21443c63e44745fe481342c7df5cf010 (patch)
tree600938ae7f9e38ffda1b88822c05caf1f42b386a /ui
parentb559fd0b02b03665b538232b7a9c7cfa6408b369 (diff)
parent17c7dcad60169b733b6852fd34ff06e4b4947f61 (diff)
A.F.I...S. [ZBXNEXT-6411] updated to latest master; resolved conflicts in :
# create/src/schema.tmpl # create/src/templates.tmpl - ours for now # include/zbxserver.h # src/libs/zbxdbcache/dbconfig.c # src/libs/zbxdbupgrade/dbupgrade_5030.c # src/libs/zbxserver/expression.c # ui/include/classes/import/CImportDataAdapter.php # ui/include/classes/import/CConfigurationImport.php # ui/include/classes/import/CConfigurationImportcompare.php # ui/include/classes/import/importers/CTemplateImporter.php # ui/include/classes/import/converters/C52ImportConverter.php # ui/include/defines.inc.php # ui/tests/unit/include/classes/import/CImportDataAdapterTest.php # ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php
Diffstat (limited to 'ui')
-rw-r--r--ui/app/controllers/CControllerMenuPopup.php7
-rw-r--r--ui/app/controllers/CControllerPopupGeneric.php4
-rw-r--r--ui/app/controllers/CControllerPopupItemTest.php80
-rw-r--r--ui/app/controllers/CControllerPopupItemTestEdit.php70
-rw-r--r--ui/app/controllers/CControllerPopupItemTestGetValue.php5
-rw-r--r--ui/app/controllers/CControllerPopupItemTestSend.php8
-rw-r--r--ui/app/controllers/CControllerPopupMassupdateItem.php3
-rw-r--r--ui/app/controllers/CControllerPopupTestTriggerExpr.php80
-rw-r--r--ui/app/controllers/CControllerPopupTriggerExpr.php442
-rw-r--r--ui/app/controllers/CControllerPopupTriggerWizard.php29
-rw-r--r--ui/app/controllers/CControllerQueueOverview.php1
-rw-r--r--ui/app/views/js/popup.massupdate.js.php4
-rw-r--r--ui/app/views/popup.triggerexpr.php64
-rw-r--r--ui/disc_prototypes.php2
-rw-r--r--ui/include/classes/api/services/CConfiguration.php3
-rw-r--r--ui/include/classes/api/services/CItemGeneral.php24
-rw-r--r--ui/include/classes/api/services/CTriggerGeneral.php233
-rw-r--r--ui/include/classes/core/ZBase.php1
-rw-r--r--ui/include/classes/data/CHistFunctionData.php317
-rw-r--r--ui/include/classes/data/CMathFunctionData.php88
-rw-r--r--ui/include/classes/export/CConfigurationExport.php16
-rw-r--r--ui/include/classes/export/CConfigurationExportBuilder.php25
-rw-r--r--ui/include/classes/export/writers/CXmlExportWriter.php1
-rw-r--r--ui/include/classes/import/CConfigurationImport.php83
-rw-r--r--ui/include/classes/import/CConfigurationImportcompare.php4
-rw-r--r--ui/include/classes/import/CImportDataAdapter.php110
-rw-r--r--ui/include/classes/import/CImportReferencer.php3
-rw-r--r--ui/include/classes/import/converters/C10TriggerConverter.php4
-rw-r--r--ui/include/classes/import/converters/C20TriggerConverter.php4
-rw-r--r--ui/include/classes/import/converters/C52AggregateItemKeyConverter.php90
-rw-r--r--ui/include/classes/import/converters/C52CalculatedItemConverter.php164
-rw-r--r--ui/include/classes/import/converters/C52EventNameConverter.php95
-rwxr-xr-x[-rw-r--r--]ui/include/classes/import/converters/C52ImportConverter.php515
-rw-r--r--ui/include/classes/import/converters/C52TriggerExpressionConverter.php768
-rw-r--r--ui/include/classes/import/importers/CTemplateImporter.php10
-rw-r--r--ui/include/classes/import/validators/C54XmlValidator.php1
-rw-r--r--ui/include/classes/items/CHelpItems.php18
-rw-r--r--ui/include/classes/macros/CMacrosResolver.php210
-rw-r--r--ui/include/classes/macros/CMacrosResolverGeneral.php84
-rw-r--r--ui/include/classes/macros/CMacrosResolverHelper.php17
-rw-r--r--ui/include/classes/parsers/C10ExpressionMacroParser.php81
-rw-r--r--ui/include/classes/parsers/C10FunctionMacroParser.php (renamed from ui/include/classes/parsers/CFunctionMacroParser.php)8
-rw-r--r--ui/include/classes/parsers/C10FunctionParser.php (renamed from ui/include/classes/parsers/CFunctionParser.php)4
-rw-r--r--ui/include/classes/parsers/C10TriggerExpression.php (renamed from ui/include/classes/parsers/CTriggerExpression.php)104
-rw-r--r--ui/include/classes/parsers/CExpressionMacroFunctionParser.php8
-rw-r--r--ui/include/classes/parsers/CExpressionMacroParser.php54
-rw-r--r--ui/include/classes/parsers/CExpressionParser.php867
-rw-r--r--ui/include/classes/parsers/CFilterParser.php530
-rw-r--r--ui/include/classes/parsers/CHistFunctionParser.php402
-rw-r--r--ui/include/classes/parsers/CLLDMacroFunctionParser.php4
-rw-r--r--ui/include/classes/parsers/CMacroFunctionParser.php4
-rw-r--r--ui/include/classes/parsers/CParser.php11
-rw-r--r--ui/include/classes/parsers/CPeriodParser.php131
-rw-r--r--ui/include/classes/parsers/CQueryParser.php208
-rw-r--r--ui/include/classes/parsers/CRelativeTimeParser.php110
-rw-r--r--ui/include/classes/parsers/results/C10TriggerExprParserResult.php (renamed from ui/include/classes/parsers/results/CTriggerExprParserResult.php)5
-rw-r--r--ui/include/classes/parsers/results/CExpressionParserResult.php122
-rw-r--r--ui/include/classes/triggers/CTextTriggerConstructor.php76
-rw-r--r--ui/include/classes/validators/CApiInputValidator.php42
-rw-r--r--ui/include/classes/validators/CExpressionValidator.php221
-rw-r--r--ui/include/classes/validators/CFunctionValidator.php693
-rw-r--r--ui/include/classes/validators/CHistFunctionValidator.php348
-rw-r--r--ui/include/classes/validators/CMathFunctionValidator.php72
-rw-r--r--ui/include/defines.inc.php7
-rw-r--r--ui/include/forms.inc.php24
-rw-r--r--ui/include/items.inc.php2
-rw-r--r--ui/include/schema.inc.php6
-rw-r--r--ui/include/triggers.inc.php405
-rw-r--r--ui/include/views/configuration.host.discovery.list.php4
-rw-r--r--ui/include/views/js/common.item.edit.js.php7
-rw-r--r--ui/include/views/js/configuration.item.edit.js.php3
-rw-r--r--ui/include/views/js/configuration.item.prototype.edit.js.php1
-rw-r--r--ui/include/views/js/itemtest.js.php1
-rw-r--r--ui/items.php10
-rw-r--r--ui/js/init.js3
-rw-r--r--ui/tests/api_json/data/data_test.sql300
-rw-r--r--ui/tests/api_json/testConfiguration.php3
-rw-r--r--ui/tests/api_json/testTriggerValidation.php68
-rw-r--r--ui/tests/bootstrap.php.template3
-rw-r--r--ui/tests/include/CTest.php13
-rw-r--r--ui/tests/include/helpers/CDataHelper.php83
-rw-r--r--ui/tests/selenium/common/testItemTest.php23
-rw-r--r--ui/tests/selenium/data/data_test.sql162
-rw-r--r--ui/tests/selenium/testFormItem.php37
-rw-r--r--ui/tests/selenium/testFormItemPrototype.php38
-rw-r--r--ui/tests/selenium/testFormTagsTrigger.php2
-rw-r--r--ui/tests/selenium/testFormTagsTriggerPrototype.php2
-rw-r--r--ui/tests/selenium/testFormTrigger.php186
-rw-r--r--ui/tests/selenium/testFormTriggerPrototype.php178
-rw-r--r--ui/tests/selenium/testFormWeb.php2
-rw-r--r--ui/tests/selenium/testInheritanceTrigger.php4
-rw-r--r--ui/tests/selenium/testInheritanceTriggerPrototype.php4
-rw-r--r--ui/tests/selenium/testTemplateInheritance.php8
-rw-r--r--ui/tests/unit/bootstrap.php1
-rw-r--r--ui/tests/unit/include/classes/import/CImportDataAdapterTest.php37
-rw-r--r--ui/tests/unit/include/classes/import/converters/C52AggregateItemKeyConverterTest.php95
-rw-r--r--ui/tests/unit/include/classes/import/converters/C52EventNameConverterTest.php92
-rw-r--r--ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php547
-rw-r--r--ui/tests/unit/include/classes/import/converters/C52TriggerExpressionConverterTest.php540
-rw-r--r--ui/tests/unit/include/classes/parsers/C10FunctionMacroParserTest.php (renamed from ui/tests/unit/include/classes/parsers/CFunctionMacroParserTest.php)6
-rw-r--r--ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php (renamed from ui/tests/unit/include/classes/parsers/CFunctionParserTest.php)110
-rw-r--r--ui/tests/unit/include/classes/parsers/C10TriggerExpressionTest.php (renamed from ui/tests/unit/include/classes/parsers/CTriggerExpressionTest.php)106
-rw-r--r--ui/tests/unit/include/classes/parsers/CExpressionMacroFunctionParserTest.php102
-rw-r--r--ui/tests/unit/include/classes/parsers/CExpressionMacroParserTest.php107
-rw-r--r--ui/tests/unit/include/classes/parsers/CExpressionParserTest.php2730
-rw-r--r--ui/tests/unit/include/classes/parsers/CFilterParserTest.php379
-rw-r--r--ui/tests/unit/include/classes/parsers/CHistFunctionParserTest.php1201
-rw-r--r--ui/tests/unit/include/classes/parsers/CLLDMacroFunctionParserTest.php6
-rw-r--r--ui/tests/unit/include/classes/parsers/CPeriodParserTest.php245
-rw-r--r--ui/tests/unit/include/classes/parsers/CQueryParserTest.php427
-rw-r--r--ui/tests/unit/include/classes/parsers/CRelativeTimeParserTest.php179
-rw-r--r--ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php116
-rw-r--r--ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php102
-rw-r--r--ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php13
-rw-r--r--ui/tests/unit/include/classes/validators/CExpressionValidatorTest.php131
-rw-r--r--ui/tests/unit/include/classes/validators/CFunctionValidatorTest.php876
-rw-r--r--ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php534
-rw-r--r--ui/tests/unit/include/triggerExpressionReplaceHostTest.php45
-rw-r--r--ui/tests/unit/include/triggers/GetExpressionTreeTest.php12
119 files changed, 14215 insertions, 3905 deletions
diff --git a/ui/app/controllers/CControllerMenuPopup.php b/ui/app/controllers/CControllerMenuPopup.php
index 9e1f4802fa5..e7bb29ea293 100644
--- a/ui/app/controllers/CControllerMenuPopup.php
+++ b/ui/app/controllers/CControllerMenuPopup.php
@@ -261,7 +261,12 @@ class CControllerMenuPopup extends CController {
}
foreach ($db_trigger['functions'] as $function) {
- if (!str_in_array($function['function'], ['regexp', 'iregexp'])) {
+ $parameters = array_map(function ($param) {
+ return trim($param, ' "');
+ }, explode(',', $function['parameter']));
+
+ if ($function['function'] !== 'find' || !array_key_exists(2, $parameters)
+ || !in_array($parameters[2], ['regexp', 'iregexp'])) {
continue 2;
}
}
diff --git a/ui/app/controllers/CControllerPopupGeneric.php b/ui/app/controllers/CControllerPopupGeneric.php
index 42d60d4a0b4..e4d66845f14 100644
--- a/ui/app/controllers/CControllerPopupGeneric.php
+++ b/ui/app/controllers/CControllerPopupGeneric.php
@@ -47,7 +47,7 @@ class CControllerPopupGeneric extends CController {
* @var array
*/
const ALLOWED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_AGGREGATE, ITEM_TYPE_IPMI, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_JMX
+ ITEM_TYPE_IPMI, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_JMX
];
/**
@@ -1115,7 +1115,7 @@ class CControllerPopupGeneric extends CController {
case 'item_prototypes':
$options += [
'output' => ['itemid', 'hostid', 'name', 'key_', 'flags', 'type', 'value_type', 'status'],
- 'selectHosts' => ['name'],
+ 'selectHosts' => ['name', 'host'],
'templated' => $this->hasInput('templated_hosts') ? true : null
];
diff --git a/ui/app/controllers/CControllerPopupItemTest.php b/ui/app/controllers/CControllerPopupItemTest.php
index ee52411454f..b83c7621e14 100644
--- a/ui/app/controllers/CControllerPopupItemTest.php
+++ b/ui/app/controllers/CControllerPopupItemTest.php
@@ -42,8 +42,8 @@ abstract class CControllerPopupItemTest extends CController {
*
* @var array
*/
- private static $testable_item_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_AGGREGATE,
- ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX,
+ private static $testable_item_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_EXTERNAL,
+ ITEM_TYPE_DB_MONITOR, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX,
ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
];
@@ -100,7 +100,7 @@ abstract class CControllerPopupItemTest extends CController {
* @var array
*/
protected $item_types_has_key_mandatory = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_AGGREGATE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_IPMI,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_IPMI,
ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED
];
@@ -543,12 +543,6 @@ abstract class CControllerPopupItemTest extends CController {
}
break;
- case ITEM_TYPE_AGGREGATE:
- $data += [
- 'key' => $input['key']
- ];
- break;
-
case ITEM_TYPE_EXTERNAL:
$data += [
'key' => $input['key']
@@ -1072,12 +1066,14 @@ abstract class CControllerPopupItemTest extends CController {
return $formula;
}
- $expression_data = new CTriggerExpression([
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => ($this->preproc_item instanceof CItemPrototype),
'calculated' => true,
- 'lldmacros' => ($this->preproc_item instanceof CItemPrototype)
+ 'host_macro' => true,
+ 'empty_host' => true
]);
- if (($result = $expression_data->parse($formula)) === false) {
+ if ($expression_parser->parse($formula) != CParser::PARSE_SUCCESS) {
// Cannot parse a calculated item formula. Return as is.
return $formula;
}
@@ -1085,29 +1081,61 @@ abstract class CControllerPopupItemTest extends CController {
$expression = [];
$pos_left = 0;
- foreach ($result->getTokens() as $token) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes([
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_LLD_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_STRING,
+ CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION
+ ]);
+ foreach ($tokens as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- case CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO:
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ case CExpressionParserResult::TOKEN_TYPE_LLD_MACRO:
if ($pos_left != $token['pos']) {
$expression[] = substr($formula, $pos_left, $token['pos'] - $pos_left);
}
$pos_left = $token['pos'] + $token['length'];
+
+ $expression[] = array_key_exists($token['match'], $macros_posted)
+ ? CExpressionParser::quoteString($macros_posted[$token['match']], false)
+ : $token['match'];
break;
- }
- switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- case CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO:
- $expression[] = array_key_exists($token['value'], $macros_posted)
- ? CTriggerExpression::quoteString($macros_posted[$token['value']], false)
- : $token['value'];
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ if ($pos_left != $token['pos']) {
+ $expression[] = substr($formula, $pos_left, $token['pos'] - $pos_left);
+ }
+ $pos_left = $token['pos'] + $token['length'];
+
+ $string = strtr(CExpressionParser::unquoteString($token['match']), $macros_posted);
+ $expression[] = CExpressionParser::quoteString($string, false, true);
break;
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
- $string = strtr($token['data']['string'], $macros_posted);
- $expression[] = CTriggerExpression::quoteString($string, false, true);
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ foreach ($token['data']['parameters'][0]['data']['filter']['tokens'] as $filter_token) {
+ switch ($filter_token['type']) {
+ case CFilterParser::TOKEN_TYPE_USER_MACRO:
+ case CFilterParser::TOKEN_TYPE_LLD_MACRO:
+ if ($pos_left != $filter_token['pos']) {
+ $expression[] = substr($formula, $pos_left, $filter_token['pos'] - $pos_left);
+ }
+ $pos_left = $filter_token['pos'] + $filter_token['length'];
+
+ $string = strtr($filter_token['match'], $macros_posted);
+ $expression[] = CFilterParser::quoteString($string);
+ break;
+
+ case CFilterParser::TOKEN_TYPE_STRING:
+ if ($pos_left != $filter_token['pos']) {
+ $expression[] = substr($formula, $pos_left, $filter_token['pos'] - $pos_left);
+ }
+ $pos_left = $filter_token['pos'] + $filter_token['length'];
+
+ $string = strtr(CFilterParser::unquoteString($filter_token['match']), $macros_posted);
+ $expression[] = CFilterParser::quoteString($string);
+ break;
+ }
+ }
break;
}
}
diff --git a/ui/app/controllers/CControllerPopupItemTestEdit.php b/ui/app/controllers/CControllerPopupItemTestEdit.php
index abddd97d4ac..63e17b99988 100644
--- a/ui/app/controllers/CControllerPopupItemTestEdit.php
+++ b/ui/app/controllers/CControllerPopupItemTestEdit.php
@@ -41,7 +41,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
'interfaceid' => 'db interface.interfaceid',
'ipmi_sensor' => 'string',
'itemid' => 'db items.itemid',
- 'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]),
+ 'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]),
'jmx_endpoint' => 'string',
'output_format' => 'in '.implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]),
'params_ap' => 'string',
@@ -91,20 +91,6 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
error(_s('Incorrect value for field "%1$s": %2$s.', 'key_', $item_key_parser->getError()));
$ret = false;
}
- elseif ($this->item_type == ITEM_TYPE_AGGREGATE) {
- $params_num = $item_key_parser->getParamsNum();
-
- if (!str_in_array($item_key_parser->getKey(), ['grpmax', 'grpmin', 'grpsum', 'grpavg'])
- || $params_num > 4 || $params_num < 3
- || ($params_num == 3 && $item_key_parser->getParam(2) !== 'last')
- || !str_in_array($item_key_parser->getParam(2),
- ['last', 'min', 'max', 'avg', 'sum', 'count'])) {
- error(_s('Key "%1$s" does not match <grpmax|grpmin|grpsum|grpavg>["Host group(s)", "Item key",'.
- ' "<last|min|max|avg|sum|count>", "parameter"].', $item_key_parser->getKey()
- ));
- $ret = false;
- }
- }
}
/*
@@ -173,22 +159,54 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
foreach (array_keys(array_intersect_key($inputs, $this->macros_by_item_props)) as $field) {
// Special processing for calculated item formula.
if ($field === 'params_f') {
- $expression_data = new CTriggerExpression(['calculated' => true, 'lldmacros' => $support_lldmacros]);
-
- if (($result = $expression_data->parse($inputs[$field])) !== false) {
- foreach ($result->getTokens() as $token) {
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => $support_lldmacros,
+ 'calculated' => true,
+ 'host_macro' => true,
+ 'empty_host' => true
+ ]);
+
+ if ($expression_parser->parse($inputs[$field]) == CParser::PARSE_SUCCESS) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes([
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_LLD_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_STRING,
+ CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION
+ ]);
+ foreach ($tokens as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- $texts_support_user_macros[] = $token['value'];
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ $texts_support_user_macros[] = $token['match'];
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_LLD_MACRO:
+ $texts_support_lld_macros[] = $token['match'];
break;
- case CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO:
- $texts_support_lld_macros[] = $token['value'];
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ $text = CExpressionParser::unquoteString($token['match']);
+ $texts_support_user_macros[] = $text;
+ $texts_support_lld_macros[] = $text;
break;
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
- $texts_support_user_macros[] = $token['data']['string'];
- $texts_support_lld_macros[] = $token['data']['string'];
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ foreach ($token['data']['parameters'][0]['data']['filter']['tokens'] as $filter_token) {
+ switch ($filter_token['type']) {
+ case CFilterParser::TOKEN_TYPE_USER_MACRO:
+ $texts_support_user_macros[] = $filter_token['match'];
+ break;
+
+ case CFilterParser::TOKEN_TYPE_LLD_MACRO:
+ $texts_support_lld_macros[] = $filter_token['match'];
+ break;
+
+ case CFilterParser::TOKEN_TYPE_STRING:
+ $text = CFilterParser::unquoteString($filter_token['match']);
+ $texts_support_user_macros[] = $text;
+ $texts_support_lld_macros[] = $text;
+ break;
+ }
+ }
break;
}
}
diff --git a/ui/app/controllers/CControllerPopupItemTestGetValue.php b/ui/app/controllers/CControllerPopupItemTestGetValue.php
index e84de6d2480..1536ff035d2 100644
--- a/ui/app/controllers/CControllerPopupItemTestGetValue.php
+++ b/ui/app/controllers/CControllerPopupItemTestGetValue.php
@@ -143,9 +143,12 @@ class CControllerPopupItemTestGetValue extends CControllerPopupItemTest {
// Apply effective macros values to properties.
$data = $this->resolveItemPropertyMacros($data);
- if ($this->item_type != ITEM_TYPE_AGGREGATE && $this->item_type != ITEM_TYPE_CALCULATED) {
+ if ($this->item_type != ITEM_TYPE_CALCULATED) {
unset($data['value_type']);
}
+ else {
+ $data['hostid'] = $this->getInput('hostid');
+ }
// Rename fields according protocol.
$data = CArrayHelper::renameKeys($data, [
diff --git a/ui/app/controllers/CControllerPopupItemTestSend.php b/ui/app/controllers/CControllerPopupItemTestSend.php
index 0deb338e0de..074762e4db5 100644
--- a/ui/app/controllers/CControllerPopupItemTestSend.php
+++ b/ui/app/controllers/CControllerPopupItemTestSend.php
@@ -69,7 +69,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
'key' => 'string',
'interface' => 'array',
'ipmi_sensor' => 'string',
- 'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]),
+ 'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]),
'jmx_endpoint' => 'string',
'macros' => 'array',
'output_format' => 'in '.implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]),
@@ -293,6 +293,10 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
$item_test_data['parameters'] = $this->transformParametersFields($item_test_data['parameters']);
}
+ if ($item_test_data['type'] == ITEM_TYPE_CALCULATED) {
+ $item_test_data['hostid'] = $this->getInput('hostid');
+ }
+
// Only non-empty fields need to be sent to server.
$item_test_data = $this->unsetEmptyValues($item_test_data);
@@ -304,7 +308,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
$item_test_data['status_codes'] = '';
}
- if ($this->item_type != ITEM_TYPE_AGGREGATE && $this->item_type != ITEM_TYPE_CALCULATED) {
+ if ($this->item_type != ITEM_TYPE_CALCULATED) {
unset($item_test_data['value_type']);
}
diff --git a/ui/app/controllers/CControllerPopupMassupdateItem.php b/ui/app/controllers/CControllerPopupMassupdateItem.php
index 3ce63ffced1..2a5238468ca 100644
--- a/ui/app/controllers/CControllerPopupMassupdateItem.php
+++ b/ui/app/controllers/CControllerPopupMassupdateItem.php
@@ -191,8 +191,6 @@ class CControllerPopupMassupdateItem extends CController {
];
$this->getInputs($input, array_keys($input));
- //'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
-
if ($this->getInput('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) {
$input['trends'] = ITEM_NO_STORAGE_VALUE;
}
@@ -251,7 +249,6 @@ class CControllerPopupMassupdateItem extends CController {
}
$hosts = $this->getHostsOrTemplates();
- $host = array_shift($hosts);
if ($prototype && $hosts) {
throw new Exception();
diff --git a/ui/app/controllers/CControllerPopupTestTriggerExpr.php b/ui/app/controllers/CControllerPopupTestTriggerExpr.php
index fd3da835597..54b09958c90 100644
--- a/ui/app/controllers/CControllerPopupTestTriggerExpr.php
+++ b/ui/app/controllers/CControllerPopupTestTriggerExpr.php
@@ -29,11 +29,11 @@ class CControllerPopupTestTriggerExpr extends CController {
private $data_table_rows = [];
private $allowed_testing = true;
private $supported_token_types = [
- CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO => 1,
- CTriggerExprParserResult::TOKEN_TYPE_MACRO => 1,
- CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO => 1,
- CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO => 1,
- CTriggerExprParserResult::TOKEN_TYPE_STRING => 1
+ CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION,
+ CExpressionParserResult::TOKEN_TYPE_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_LLD_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_STRING
];
protected function init() {
@@ -55,32 +55,29 @@ class CControllerPopupTestTriggerExpr extends CController {
$this->expression = $_REQUEST['expression'];
}
- $expression_data = new CTriggerExpression();
- $result = $expression_data->parse($this->expression);
-
- if ($result) {
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ if ($expression_parser->parse($this->expression) == CParser::PARSE_SUCCESS) {
$this->macros_data = [];
- foreach ($result->getTokens() as $token) {
- if (!array_key_exists($token['type'], $this->supported_token_types)) {
- continue;
- }
-
- if ($token['type'] == CTriggerExprParserResult::TOKEN_TYPE_STRING) {
- $matched_macros = CMacrosResolverGeneral::extractMacros([$token['data']['string']], [
- 'macros' => [
- 'trigger' => ['{TRIGGER.VALUE}']
- ],
- 'lldmacros' => true,
- 'usermacros' => true
- ]);
+ foreach ($expression_parser->getResult()->getTokensOfTypes($this->supported_token_types) as $token) {
+ if ($token['type'] == CExpressionParserResult::TOKEN_TYPE_STRING) {
+ $matched_macros = CMacrosResolverGeneral::extractMacros(
+ [CExpressionParser::unquoteString($token['match'])],
+ [
+ 'macros' => [
+ 'trigger' => ['{TRIGGER.VALUE}']
+ ],
+ 'lldmacros' => true,
+ 'usermacros' => true
+ ]
+ );
$macros = array_merge(array_keys($matched_macros['usermacros'] + $matched_macros['lldmacros']),
$matched_macros['macros']['trigger']
);
}
else {
- $macros = [$token['value']];
+ $macros = [$token['match']];
}
foreach ($macros as $macro) {
@@ -159,43 +156,28 @@ class CControllerPopupTestTriggerExpr extends CController {
$mapping = [];
$expressions = [];
- $expression_data = new CTriggerExpression();
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
foreach ($expression_html_tree as $e) {
$original_expression = $e['expression']['value'];
- $result = $expression_data->parse($original_expression);
-
- if ($result) {
- $expression = [];
- $pos_left = 0;
+ if ($expression_parser->parse($original_expression) == CParser::PARSE_SUCCESS) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes($this->supported_token_types);
+ $expression = $original_expression;
- foreach ($result->getTokens() as $token) {
- if (!array_key_exists($token['type'], $this->supported_token_types)) {
- continue;
- }
-
- if ($pos_left != $token['pos']) {
- $expression[] = substr($original_expression, $pos_left, $token['pos'] - $pos_left);
- }
- $pos_left = $token['pos'] + $token['length'];
-
- if ($token['type'] == CTriggerExprParserResult::TOKEN_TYPE_STRING) {
- $value = strtr($token['data']['string'], $this->macros_data);
- $expression[] = CTriggerExpression::quoteString($value, false, true);
+ for ($token = end($tokens); $token; $token = prev($tokens)) {
+ if ($token['type'] == CExpressionParserResult::TOKEN_TYPE_STRING) {
+ $value = strtr(CExpressionParser::unquoteString($token['match']), $this->macros_data);
+ $value = CExpressionParser::quoteString($value, false, true);
}
else {
- $value = strtr($token['value'], $this->macros_data);
- $expression[] = CTriggerExpression::quoteString($value, false);
+ $value = strtr($token['match'], $this->macros_data);
+ $value = CExpressionParser::quoteString($value, false);
}
- }
- if ($pos_left != strlen($original_expression)) {
- $expression[] = substr($original_expression, $pos_left);
+ $expression = substr_replace($expression, $value, $token['pos'], $token['length']);
}
- $expression = implode('', $expression);
-
$mapping[$expression][] = $original_expression;
$expressions[] = $expression;
}
diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php
index 424448d8d29..aaaee743836 100644
--- a/ui/app/controllers/CControllerPopupTriggerExpr.php
+++ b/ui/app/controllers/CControllerPopupTriggerExpr.php
@@ -28,6 +28,7 @@ class CControllerPopupTriggerExpr extends CController {
private $param2SecCount = [];
private $param2SecMode = [];
private $param3SecVal = [];
+ private $param_find = [];
private $param3SecPercent = [];
private $paramSecIntCount = [];
private $paramForecast = [];
@@ -39,6 +40,7 @@ class CControllerPopupTriggerExpr extends CController {
private $allowedTypesInt = [];
private $functions = [];
private $operators = [];
+ private $period_optional = [];
protected function init() {
$this->disableSIDvalidation();
@@ -68,6 +70,20 @@ class CControllerPopupTriggerExpr extends CController {
]
];
+ $this->period_optional = [
+ 'last' => [
+ 'C' => _('Last of').' (T)',
+ 'T' => T_ZBX_INT,
+ 'M' => $this->metrics,
+ 'A' => false
+ ],
+ 'shift' => [
+ 'C' => _('Time shift'),
+ 'T' => T_ZBX_INT,
+ 'A' => false
+ ]
+ ];
+
$this->param1Period = [
'last' => [
'C' => _('Last of').' (T)',
@@ -91,7 +107,7 @@ class CControllerPopupTriggerExpr extends CController {
$this->param1Str = [
'pattern' => [
- 'C' => 'T',
+ 'C' => 'V',
'T' => T_ZBX_STR,
'A' => false
]
@@ -131,19 +147,32 @@ class CControllerPopupTriggerExpr extends CController {
'M' => $this->metrics,
'A' => true
],
+ 'shift' => [
+ 'C' => _('Time shift'),
+ 'T' => T_ZBX_INT,
+ 'A' => false
+ ],
+ 'o' => [
+ 'C' => 'O',
+ 'T' => T_ZBX_STR,
+ 'A' => false
+ ],
'v' => [
'C' => 'V',
'T' => T_ZBX_STR,
'A' => false
- ],
+ ]
+ ];
+
+ $this->param_find = [
'o' => [
'C' => 'O',
'T' => T_ZBX_STR,
'A' => false
],
- 'shift' => [
- 'C' => _('Time shift'),
- 'T' => T_ZBX_INT,
+ 'v' => [
+ 'C' => 'V',
+ 'T' => T_ZBX_STR,
'A' => false
]
];
@@ -174,15 +203,15 @@ class CControllerPopupTriggerExpr extends CController {
'M' => $this->metrics,
'A' => true
],
- 'mask' => [
- 'C' => _('Mask'),
- 'T' => T_ZBX_STR,
- 'A' => true
- ],
'shift' => [
'C' => _('Time shift'),
'T' => T_ZBX_INT,
'A' => false
+ ],
+ 'mask' => [
+ 'C' => _('Mask'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
]
];
@@ -267,8 +296,8 @@ class CControllerPopupTriggerExpr extends CController {
];
$this->functions = [
- 'abschange' => [
- 'description' => _('abschange() - Absolute difference between last and previous value'),
+ 'abs' => [
+ 'description' => _('abs() - Absolute value'),
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
@@ -278,12 +307,6 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesNumeric,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
- 'delta' => [
- 'description' => _('delta() - Difference between MAX and MIN value of a period T'),
- 'params' => $this->param1SecCount,
- 'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
- ],
'change' => [
'description' => _('change() - Difference between last and previous value'),
'allowed_types' => $this->allowedTypesAny,
@@ -295,8 +318,9 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
- 'diff' => [
- 'description' => _('diff() - Difference between last and preceding values (1 - true, 0 - false)'),
+ 'find' => [
+ 'description' => _('find() - Check occurrence of pattern V (which fulfill operator O) for period T (1 - match, 0 - no match)'),
+ 'params' => $this->period_optional + $this->param_find,
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>']
],
@@ -306,6 +330,11 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
+ 'length' => [
+ 'description' => _('length() - Length of last (most recent) T value in characters'),
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ ],
'max' => [
'description' => _('max() - Maximum value for period T'),
'params' => $this->param1SecCount,
@@ -324,23 +353,6 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesNumeric,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
- 'prev' => [
- 'description' => _('prev() - Previous value'),
- 'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
- ],
- 'str' => [
- 'description' => _('str() - Find string V in last (most recent) value (1 - found, 0 - not found)'),
- 'params' => $this->param2SecCount,
- 'allowed_types' => $this->allowedTypesStr,
- 'operators' => ['=', '<>']
- ],
- 'strlen' => [
- 'description' => _('strlen() - Length of last (most recent) T value in characters'),
- 'params' => $this->param1SecCount,
- 'allowed_types' => $this->allowedTypesStr,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
- ],
'sum' => [
'description' => _('sum() - Sum of values of a period T'),
'params' => $this->param1SecCount,
@@ -368,32 +380,21 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesNumeric,
'operators' => ['=', '<>']
],
- 'regexp' => [
- 'description' => _('regexp() - Regular expression V matching last value in period T (1 - match, 0 - no match)'),
- 'params' => $this->param2SecCount,
- 'allowed_types' => $this->allowedTypesStr,
- 'operators' => ['=', '<>']
- ],
- 'iregexp' => [
- 'description' => _('iregexp() - Regular expression V matching last value in period T (non case-sensitive; 1 - match, 0 - no match)'),
- 'params' => $this->param2SecCount,
- 'allowed_types' => $this->allowedTypesStr,
- 'operators' => ['=', '<>']
- ],
'logeventid' => [
- 'description' => _('logeventid() - Event ID of last log entry matching regular expression T (1 - match, 0 - no match)'),
- 'params' => $this->param1Str,
+ 'description' => _('logeventid() - Event ID of last log entry matching regular expression V for period T (1 - match, 0 - no match)'),
+ 'params' => $this->period_optional + $this->param1Str,
'allowed_types' => $this->allowedTypesLog,
'operators' => ['=', '<>']
],
'logseverity' => [
- 'description' => _('logseverity() - Log severity of the last log entry'),
+ 'description' => _('logseverity() - Log severity of the last log entry for period T'),
+ 'params' => $this->period_optional,
'allowed_types' => $this->allowedTypesLog,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
'logsource' => [
- 'description' => _('logsource() - Log source of the last log entry matching parameter T (1 - match, 0 - no match)'),
- 'params' => $this->param1Str,
+ 'description' => _('logsource() - Log source of the last log entry matching parameter V for period T (1 - match, 0 - no match)'),
+ 'params' => $this->period_optional + $this->param1Str,
'allowed_types' => $this->allowedTypesLog,
'operators' => ['=', '<>']
],
@@ -413,8 +414,8 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>']
],
- 'band' => [
- 'description' => _('band() - Bitwise AND of last (most recent) T value and mask'),
+ 'bitand' => [
+ 'description' => _('bitand() - Bitwise AND of last (most recent) T value and mask'),
'params' => $this->paramSecIntCount,
'allowed_types' => $this->allowedTypesInt,
'operators' => ['=', '<>']
@@ -443,12 +444,6 @@ class CControllerPopupTriggerExpr extends CController {
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>', '>', '<', '>=', '<=']
],
- 'trenddelta' => [
- 'description' => _('trenddelta() - Difference between MAX and MIN value of a period T with exact period shift'),
- 'params' => $this->param1Period,
- 'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
- ],
'trendmax' => [
'description' => _('trendmax() - Maximum value for period T with exact period shift'),
'params' => $this->param1Period,
@@ -521,6 +516,9 @@ class CControllerPopupTriggerExpr extends CController {
}
protected function doAction() {
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ $expression_validator = new CExpressionValidator();
+
$itemid = $this->getInput('itemid', 0);
$function = $this->getInput('function', 'last');
$operator = $this->getInput('operator', '=');
@@ -530,85 +528,150 @@ class CControllerPopupTriggerExpr extends CController {
$params = $this->getInput('params', []);
$value = $this->getInput('value', 0);
+ $item = false;
+
// Opening the popup when editing an expression in the trigger constructor.
if (($dstfld1 === 'expr_temp' || $dstfld1 === 'recovery_expr_temp') && $expression !== '') {
$expression = utf8RawUrlDecode($expression);
- $expression_data = new CTriggerExpression();
- $result = $expression_data->parse($expression);
-
- if ($result) {
- $function_macro_tokens = $result->getTokensByType(
- CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO
- );
+ if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
+ $math_function_token = null;
+ $hist_function_token = null;
+ $function_token_index = null;
+ $tokens = $expression_parser->getResult()->getTokens();
+
+ foreach ($tokens as $index => $token) {
+ switch ($token['type']) {
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ $math_function_token = $token;
+ $function_token_index = $index;
+
+ foreach ($token['data']['parameters'] as $parameter) {
+ foreach ($parameter['data']['tokens'] as $parameter_token) {
+ if ($parameter_token['type'] == CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION) {
+ $hist_function_token = $parameter_token;
+ break 2;
+ }
+ }
+ }
+ break 2;
- if ($function_macro_tokens) {
- $function_macro_token = $function_macro_tokens[0];
- $function = $function_macro_token['data']['functionName'];
-
- // Determine param type.
- $params = $function_macro_token['data']['functionParams'];
- $param_number = in_array($function, ['regexp', 'iregexp', 'str']) ? 1 : 0;
- if (array_key_exists($param_number, $params) && is_string($params[$param_number])
- && $params[$param_number] !== '' && $params[$param_number][0] === '#'
- && !in_array($function, ['fuzzytime', 'nodata'])) {
- $param_type = PARAM_TYPE_COUNTS;
- $params[$param_number] = substr($params[$param_number], 1);
- }
- else {
- $param_type = PARAM_TYPE_TIME;
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ $hist_function_token = $token;
+ $function_token_index = $index;
+ break 2;
}
+ }
+ if ($function_token_index !== null) {
/*
* Try to find an operator and a value.
- * The value and operator can be extracted only if they immediately follow the item function macro.
+ * The value and operator can be extracted only if they immediately follow the function.
*/
- $tokens = $result->getTokens();
- foreach ($tokens as $key => $token) {
- if ($token['type'] == CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO) {
- if (array_key_exists($key + 2, $tokens)
- && $tokens[$key + 1]['type'] == CTriggerExprParserResult::TOKEN_TYPE_OPERATOR
- && array_key_exists($function, $this->functions)
- && in_array($tokens[$key + 1]['value'],
- $this->functions[$function]['operators'])) {
- $operator = $tokens[$key + 1]['value'];
-
- $value = '';
- $i = 2;
-
- if (array_key_exists($key + 3, $tokens)
- && $tokens[$key + 2]['type'] == CTriggerExprParserResult::TOKEN_TYPE_OPERATOR) {
- $value .= $tokens[$key + 2]['value'];
- $i++;
- }
-
- $value .= ($tokens[$key + $i]['type'] == CTriggerExprParserResult::TOKEN_TYPE_STRING)
- ? $tokens[$key + $i]['data']['string']
- : $tokens[$key + $i]['value'];
+ $index = $function_token_index + 1;
+
+ if (array_key_exists($index, $tokens)
+ && $tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_OPERATOR
+ && in_array($tokens[$index]['match'], ['=', '<>', '>', '<', '>=', '<='])) {
+ $operator = $tokens[$index]['match'];
+ $index++;
+
+ if (array_key_exists($index, $tokens)) {
+ if ($tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_NUMBER
+ || $tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_MACRO
+ || $tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_USER_MACRO
+ || $tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_LLD_MACRO) {
+ $value = $tokens[$index]['match'];
}
- else {
- break;
+ elseif ($tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_STRING) {
+ $value = CExpressionParser::unquoteString($tokens[$index]['match']);
+ }
+ elseif ($tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_OPERATOR
+ && array_key_exists($index + 1, $tokens)
+ && $tokens[$index + 1]['type'] == CExpressionParserResult::TOKEN_TYPE_NUMBER) {
+ $value = '-'.$tokens[$index + 1]['match'];
}
}
}
- // Find the item.
- $item = API::Item()->get([
- 'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type'],
- 'selectHosts' => ['name'],
- 'webitems' => true,
- 'filter' => [
- 'host' => $function_macro_token['data']['host'],
- 'key_' => $function_macro_token['data']['item'],
- 'flags' => null
- ]
- ]);
-
- if (($item = reset($item)) !== false) {
- $itemid = $item['itemid'];
+ // Get function parameters.
+ $parameters = null;
+
+ if ($math_function_token) {
+ $function = $math_function_token['data']['function'];
+
+ if ($hist_function_token && $hist_function_token['data']['function'] === 'last') {
+ $parameters = $hist_function_token['data']['parameters'];
+ }
}
else {
- error(_('Unknown host item, no such item in selected host'));
+ $function = $hist_function_token['data']['function'];
+ $parameters = $hist_function_token['data']['parameters'];
+ }
+
+ if ($parameters !== null) {
+ $host = $hist_function_token['data']['parameters'][0]['data']['host'];
+ $key = $hist_function_token['data']['parameters'][0]['data']['item'];
+
+ $items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type'],
+ 'selectHosts' => ['name'],
+ 'webitems' => true,
+ 'filter' => [
+ 'host' => $host,
+ 'key_' => $key
+ ]
+ ]);
+
+ if (!$items) {
+ $items = API::ItemPrototype()->get([
+ 'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type'],
+ 'selectHosts' => ['name'],
+ 'filter' => [
+ 'host' => $host,
+ 'key_' => $key
+ ]
+ ]);
+ }
+
+ if (($item = reset($items)) === false) {
+ error(_('Unknown host item, no such item in selected host'));
+ }
+ }
+
+ $params = [];
+
+ if ($parameters !== null && array_key_exists(1, $parameters)) {
+ if ($function === "nodata" || $function === "fuzzytime") {
+ $params[] = ($parameters[1]['type'] == CHistFunctionParser::PARAM_TYPE_QUOTED)
+ ? CHistFunctionParser::unquoteParam($parameters[1]['match'])
+ : $parameters[1]['match'];
+ }
+ else {
+ if ($parameters[1]['type'] == CHistFunctionParser::PARAM_TYPE_PERIOD) {
+ $sec_num = $parameters[1]['data']['sec_num'];
+ if ($sec_num !== '' && $sec_num[0] === '#') {
+ $params[] = substr($sec_num, 1);
+ $param_type = PARAM_TYPE_COUNTS;
+ }
+ else {
+ $params[] = $sec_num;
+ $param_type = PARAM_TYPE_TIME;
+ }
+ $params[] = $parameters[1]['data']['time_shift'];
+ }
+ else {
+ $params[] = '';
+ $params[] = '';
+ }
+ }
+
+ for ($i = 2; $i < count($parameters); $i++) {
+ $parameter = $parameters[$i];
+ $params[] = $parameter['type'] == CHistFunctionParser::PARAM_TYPE_QUOTED
+ ? CHistFunctionParser::unquoteParam($parameter['match'])
+ : $parameter['match'];
+ }
}
}
}
@@ -626,10 +689,11 @@ class CControllerPopupTriggerExpr extends CController {
$item = reset($item);
}
- if ($itemid) {
+ if ($item) {
$items = CMacrosResolverHelper::resolveItemNames([$item]);
$item = $items[0];
+ $itemid = $item['itemid'];
$item_value_type = $item['value_type'];
$item_key = $item['key_'];
$item_host_data = reset($item['hosts']);
@@ -661,6 +725,7 @@ class CControllerPopupTriggerExpr extends CController {
'params' => $params,
'paramtype' => $param_type,
'item_description' => $description,
+ 'item_required' => !in_array($function, getStandaloneFunctions()),
'functions' => $this->functions,
'function' => $function,
'operator' => $operator,
@@ -701,66 +766,103 @@ class CControllerPopupTriggerExpr extends CController {
// Create and validate trigger expression before inserting it into textarea field.
if ($this->getInput('add', false)) {
try {
- if ($data['item_description']) {
- if ($data['paramtype'] == PARAM_TYPE_COUNTS
- && array_key_exists('last', $data['params'])
- && $data['params']['last'] !== '') {
- $data['params']['last'] = zbx_is_int($data['params']['last'])
- ? '#'.$data['params']['last']
- : $data['params']['last'];
+ if (in_array($function, getStandaloneFunctions())) {
+ $data['expression'] = sprintf('%s()%s%s', $function, $operator,
+ CExpressionParser::quoteString($data['value'])
+ );
+ }
+ elseif ($data['item_description']) {
+ // Quote function string parameters.
+ foreach ($data['params'] as $param_key => $param) {
+ if (!in_array($param_key, ['v', 'o', 'fit', 'mode', 'pattern'])
+ || !array_key_exists($param_key, $data['params'])
+ || $data['params'][$param_key] === '') {
+ continue;
+ }
+
+ $data['params'][$param_key] = quoteFunctionParam($param, true);
+ }
+
+ // Combine sec|#num and <time_shift|period_shift> parameters into one.
+ if (array_key_exists('last', $data['params'])) {
+ if ($data['paramtype'] == PARAM_TYPE_COUNTS && zbx_is_int($data['params']['last'])) {
+ $data['params']['last'] = '#'.$data['params']['last'];
+ }
}
- elseif ($data['paramtype'] == PARAM_TYPE_TIME && in_array($function, ['last', 'band', 'strlen'])) {
+ else {
$data['params']['last'] = '';
}
- // Quote function param.
- $quoted_params = [];
- foreach ($data['params'] as $param) {
- $quoted_params[] = quoteFunctionParam($param);
+ if (array_key_exists('shift', $data['params']) && $data['params']['shift'] !== '') {
+ $data['params']['last'] .= ':'.$data['params']['shift'];
}
+ elseif (array_key_exists('period_shift', $data['params'])
+ && $data['params']['period_shift'] !== '') {
+ $data['params']['last'] .= ':'.$data['params']['period_shift'];
+ }
+ unset($data['params']['shift'], $data['params']['period_shift']);
- $data['expression'] = sprintf('{%s:%s.%s(%s)}%s%s',
- $item_host_data['host'],
- $data['item_key'],
- $function,
- rtrim(implode(',', $quoted_params), ','),
- $operator,
- CTriggerExpression::quoteString($data['value'])
- );
-
- // Validate trigger expression.
- $trigger_expression = new CTriggerExpression();
-
- if ($trigger_expression->parse($data['expression'])) {
- $expression_data = reset($trigger_expression->expressions);
+ $mask = '';
+ if ($function === 'bitand' && array_key_exists('mask', $data['params'])) {
+ $mask = $data['params']['mask'];
+ unset($data['params']['mask']);
+ }
- // Validate trigger function.
- $trigger_function_validator = new CFunctionValidator();
- $is_valid = $trigger_function_validator->validate([
- 'function' => $expression_data['function'],
- 'functionName' => $expression_data['functionName'],
- 'functionParamList' => $expression_data['functionParamList'],
- 'valueType' => $data['itemValueType']
- ]);
+ $fn_params = rtrim(implode(',', $data['params']), ',');
- if ($is_valid === false) {
- error($trigger_function_validator->getError());
- }
+ if ($function === 'abs') {
+ $data['expression'] = sprintf('abs(last(/%s/%s)%s)%s%s',
+ $item_host_data['host'],
+ $data['item_key'],
+ ($fn_params === '') ? '' : ','.$fn_params,
+ $operator,
+ CExpressionParser::quoteString($data['value'])
+ );
}
- else {
- error($trigger_expression->error);
+ elseif ($function === 'bitand') {
+ $data['expression'] = sprintf('bitand(last(/%s/%s%s)%s)%s%s',
+ $item_host_data['host'],
+ $data['item_key'],
+ ($fn_params === '') ? '' : ','.$fn_params,
+ ($mask === '') ? '' : ','.$mask,
+ $operator,
+ CExpressionParser::quoteString($data['value'])
+ );
}
-
- // Quote function param.
- if (array_key_exists('insert', $data)) {
- foreach ($data['params'] as $pnum => $param) {
- $data['params'][$pnum] = quoteFunctionParam($param);
- }
+ elseif ($function === 'length') {
+ $data['expression'] = sprintf('length(last(/%s/%s))%s%s',
+ $item_host_data['host'],
+ $data['item_key'],
+ $operator,
+ CExpressionParser::quoteString($data['value'])
+ );
+ }
+ else {
+ $data['expression'] = sprintf('%s(/%s/%s%s)%s%s',
+ $function,
+ $item_host_data['host'],
+ $data['item_key'],
+ ($fn_params === '') ? '' : ','.$fn_params,
+ $operator,
+ CExpressionParser::quoteString($data['value'])
+ );
}
}
else {
error(_('Item not selected'));
}
+
+ if (array_key_exists('expression', $data)) {
+ // Parse and validate trigger expression.
+ if ($expression_parser->parse($data['expression']) == CParser::PARSE_SUCCESS) {
+ if (!$expression_validator->validate($expression_parser->getResult()->getTokens())) {
+ error($expression_validator->getError());
+ }
+ }
+ else {
+ error($expression_parser->getError());
+ }
+ }
}
catch (Exception $e) {
error($e->getMessage());
diff --git a/ui/app/controllers/CControllerPopupTriggerWizard.php b/ui/app/controllers/CControllerPopupTriggerWizard.php
index 0ef13b5322b..98718c81f57 100644
--- a/ui/app/controllers/CControllerPopupTriggerWizard.php
+++ b/ui/app/controllers/CControllerPopupTriggerWizard.php
@@ -111,7 +111,7 @@ class CControllerPopupTriggerWizard extends CController {
$exprs[] = array_shift($input) + array_shift($input);
}
- $constructor = new CTextTriggerConstructor(new CTriggerExpression());
+ $constructor = new CTextTriggerConstructor(new CExpressionParser(['lldmacros' => true]));
if ($this->hasInput('triggerid')) {
$page_options['triggerid'] = $this->getInput('triggerid');
@@ -121,29 +121,30 @@ class CControllerPopupTriggerWizard extends CController {
if ($this->hasInput('save')) {
$trigger_valid = true;
- $item = API::Item()->get([
+ $items = API::Item()->get([
'output' => ['key_'],
'selectHosts' => ['host'],
'itemids' => $page_options['itemid'],
+ 'editable' => true,
'limit' => 1
]);
- $item = reset($item);
- $host = reset($item['hosts']);
-
// Trigger validation.
if ($page_options['description'] === '') {
error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'), _('cannot be empty')));
$trigger_valid = false;
}
- if (!$item) {
+ if (!$items) {
error('No permissions to referred object or it does not exist!');
$trigger_valid = false;
}
- if ($exprs && ($expression = $constructor->getExpressionFromParts($host['host'], $item['key_'], $exprs))) {
- if (check_right_on_trigger_by_expression(PERM_READ_WRITE, $expression)) {
+ $item = reset($items);
+ $host = reset($item['hosts']);
+
+ if ($trigger_valid) {
+ if ($exprs && ($expression = $constructor->getExpressionFromParts($host['host'], $item['key_'], $exprs))) {
if (array_key_exists('triggerid', $page_options)) {
$triggerid = $page_options['triggerid'];
$description = $page_options['description'];
@@ -190,7 +191,7 @@ class CControllerPopupTriggerWizard extends CController {
}
// Save if no errors found.
- if (array_key_exists('triggerid', $page_options) && $trigger_valid) {
+ if (array_key_exists('triggerid', $page_options)) {
$result = API::Trigger()->update($trigger);
$audit_action = AUDIT_ACTION_UPDATE;
@@ -198,7 +199,7 @@ class CControllerPopupTriggerWizard extends CController {
error(_('Cannot update trigger'));
}
}
- elseif ($trigger_valid) {
+ else {
$result = API::Trigger()->create($trigger);
if ($result['triggerids']) {
$db_triggers = API::Trigger()->get([
@@ -214,9 +215,6 @@ class CControllerPopupTriggerWizard extends CController {
error(_('Cannot add trigger'));
}
}
- else {
- $result['triggerids'] = false;
- }
if ($result['triggerids']) {
DBstart();
@@ -229,12 +227,9 @@ class CControllerPopupTriggerWizard extends CController {
}
}
else {
- error('No permissions to referred object or it does not exist!');
+ error(_s('Field "%1$s" is mandatory.', 'expressions'));
}
}
- else {
- error(_s('Field "%1$s" is mandatory.', 'expressions'));
- }
$output = [];
if (($messages = getMessages()) !== null) {
diff --git a/ui/app/controllers/CControllerQueueOverview.php b/ui/app/controllers/CControllerQueueOverview.php
index d9c031f4ab7..2032772a325 100644
--- a/ui/app/controllers/CControllerQueueOverview.php
+++ b/ui/app/controllers/CControllerQueueOverview.php
@@ -60,7 +60,6 @@ class CControllerQueueOverview extends CController {
ITEM_TYPE_SIMPLE,
ITEM_TYPE_SNMP,
ITEM_TYPE_INTERNAL,
- ITEM_TYPE_AGGREGATE,
ITEM_TYPE_EXTERNAL,
ITEM_TYPE_DB_MONITOR,
ITEM_TYPE_HTTPAGENT,
diff --git a/ui/app/views/js/popup.massupdate.js.php b/ui/app/views/js/popup.massupdate.js.php
index 1a5fc66c675..718b97420de 100644
--- a/ui/app/views/js/popup.massupdate.js.php
+++ b/ui/app/views/js/popup.massupdate.js.php
@@ -280,8 +280,6 @@ $('#tabs').on('tabsactivate', (event, ui) => {
// Value maps.
(() => {
const valuemap = document.querySelector('#valuemap-div');
- const form = document.querySelector('#massupdate-form');
- const action = form.querySelector('#action').value;
if (!valuemap) {
return false;
@@ -449,7 +447,7 @@ function submitPopup(overlay) {
// Remove error message.
overlay.$dialogue.find('.<?= ZBX_STYLE_MSG_BAD ?>').remove();
- url = new Curl('zabbix.php', false),
+ const url = new Curl('zabbix.php', false);
url.setArgument('action', action);
url.setArgument('output', 'ajax');
diff --git a/ui/app/views/popup.triggerexpr.php b/ui/app/views/popup.triggerexpr.php
index 9f8190bda79..c64ab723e08 100644
--- a/ui/app/views/popup.triggerexpr.php
+++ b/ui/app/views/popup.triggerexpr.php
@@ -32,7 +32,6 @@ $expression_form = (new CForm())
->addVar('dstfld1', $data['dstfld1'])
->addItem((new CVar('hostid', $data['hostid']))->removeId())
->addVar('groupid', $data['groupid'])
- ->addVar('itemid', $data['itemid'])
->addItem((new CInput('submit', 'submit'))
->addStyle('display: none;')
->removeId()
@@ -65,35 +64,39 @@ if ($data['parent_discoveryid'] !== '') {
$popup_options['normal_only'] = '1';
}
-$item = [
- (new CTextBox('item_description', $data['item_description'], true))
- ->setAriaRequired()
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
- (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
- (new CButton('select', _('Select')))
- ->addClass(ZBX_STYLE_BTN_GREY)
- ->onClick('return PopUp("popup.generic",'.json_encode($popup_options).', null, this);')
-];
+if ($data['item_required']) {
+ $expression_form->addVar('itemid', $data['itemid']);
-if ($data['parent_discoveryid'] !== '') {
- $item[] = (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN);
- $item[] = (new CButton('select', _('Select prototype')))
- ->addClass(ZBX_STYLE_BTN_GREY)
- ->onClick('return PopUp("popup.generic",'.
- json_encode([
- 'srctbl' => 'item_prototypes',
- 'srcfld1' => 'itemid',
- 'srcfld2' => 'name',
- 'dstfrm' => $expression_form->getName(),
- 'dstfld1' => 'itemid',
- 'dstfld2' => 'item_description',
- 'parent_discoveryid' => $data['parent_discoveryid']
- ]).', null, this);'
- )
- ->removeId();
-}
+ $item = [
+ (new CTextBox('item_description', $data['item_description'], true))
+ ->setAriaRequired()
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
+ (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
+ (new CButton('select', _('Select')))
+ ->addClass(ZBX_STYLE_BTN_GREY)
+ ->onClick('return PopUp("popup.generic",'.json_encode($popup_options).', null, this);')
+ ];
+
+ if ($data['parent_discoveryid'] !== '') {
+ $item[] = (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN);
+ $item[] = (new CButton('select', _('Select prototype')))
+ ->addClass(ZBX_STYLE_BTN_GREY)
+ ->onClick('return PopUp("popup.generic",'.
+ json_encode([
+ 'srctbl' => 'item_prototypes',
+ 'srcfld1' => 'itemid',
+ 'srcfld2' => 'name',
+ 'dstfrm' => $expression_form->getName(),
+ 'dstfld1' => 'itemid',
+ 'dstfld2' => 'item_description',
+ 'parent_discoveryid' => $data['parent_discoveryid']
+ ]).', null, this);'
+ )
+ ->removeId();
+ }
-$expression_form_list->addRow((new CLabel(_('Item'), 'item_description'))->setAsteriskMark(), $item);
+ $expression_form_list->addRow((new CLabel(_('Item'), 'item_description'))->setAsteriskMark(), $item);
+}
$function_select = (new CSelect('function'))
->setFocusableElementId('label-function')
@@ -124,7 +127,7 @@ if (array_key_exists('params', $data['functions'][$data['selectedFunction']])) {
if (in_array($param_name, ['last'])) {
if (array_key_exists('M', $param_function)) {
- if (in_array($data['selectedFunction'], ['last', 'band', 'strlen'])) {
+ if (in_array($data['selectedFunction'], ['last', 'bitand', 'strlen'])) {
$param_type_element = $param_function['M'][PARAM_TYPE_COUNTS];
$label = $param_function['C'];
$expression_form->addItem((new CVar('paramtype', PARAM_TYPE_COUNTS))->removeId());
@@ -152,6 +155,9 @@ if (array_key_exists('params', $data['functions'][$data['selectedFunction']])) {
if ($param_name === 'period_shift') {
$param_field->setAttribute('placeholder', 'now/h');
}
+ elseif ($param_name === 'shift') {
+ $param_field->setAttribute('placeholder', 'now-h');
+ }
$expression_form_list->addRow($label, [
$param_field,
diff --git a/ui/disc_prototypes.php b/ui/disc_prototypes.php
index 35256aed07a..73c811b5dcc 100644
--- a/ui/disc_prototypes.php
+++ b/ui/disc_prototypes.php
@@ -66,7 +66,7 @@ $fields = [
'discover' => [T_ZBX_INT, O_OPT, null, IN([ZBX_PROTOTYPE_DISCOVER, ZBX_PROTOTYPE_NO_DISCOVER]), null],
'type' => [T_ZBX_INT, O_OPT, null,
IN([-1, ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE,
- ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE,
+ ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMPTRAP,
ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
diff --git a/ui/include/classes/api/services/CConfiguration.php b/ui/include/classes/api/services/CConfiguration.php
index d235039dba1..27750a58190 100644
--- a/ui/include/classes/api/services/CConfiguration.php
+++ b/ui/include/classes/api/services/CConfiguration.php
@@ -45,8 +45,7 @@ class CConfiguration extends CApiService {
'images' => ['type' => API_IDS],
'maps' => ['type' => API_IDS],
'mediaTypes' => ['type' => API_IDS],
- 'templates' => ['type' => API_IDS],
- 'valueMaps' => ['type' => API_IDS]
+ 'templates' => ['type' => API_IDS]
]]
]];
if (!CApiInputValidator::validate($api_input_rules, $params, '/', $error)) {
diff --git a/ui/include/classes/api/services/CItemGeneral.php b/ui/include/classes/api/services/CItemGeneral.php
index 76bde61af60..a6415fab9c1 100644
--- a/ui/include/classes/api/services/CItemGeneral.php
+++ b/ui/include/classes/api/services/CItemGeneral.php
@@ -334,7 +334,8 @@ abstract class CItemGeneral extends CApiService {
if ($fullItem['type'] == ITEM_TYPE_CALCULATED) {
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0]
+ 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0, 'length' => DB::getFieldLength('items', 'params')],
+ 'value_type' => ['type' => API_INT32, 'in' => ITEM_VALUE_TYPE_UINT64.','.ITEM_VALUE_TYPE_FLOAT]
]];
$data = array_intersect_key($item, $api_input_rules['fields']);
@@ -448,27 +449,6 @@ abstract class CItemGeneral extends CApiService {
);
}
- // parameters
- if ($fullItem['type'] == ITEM_TYPE_AGGREGATE) {
- $params_num = $item_key_parser->getParamsNum();
-
- if (!str_in_array($item_key_parser->getKey(), ['grpmax', 'grpmin', 'grpsum', 'grpavg'])
- || $params_num > 4 || $params_num < 3
- || ($params_num == 3 && $item_key_parser->getParam(2) !== 'last')
- || !str_in_array($item_key_parser->getParam(2), ['last', 'min', 'max', 'avg', 'sum', 'count'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Key "%1$s" does not match <grpmax|grpmin|grpsum|grpavg>["Host group(s)", "Item key",'.
- ' "<last|min|max|avg|sum|count>", "parameter"].', $item_key_parser->getKey()));
- }
- }
-
- // type of information
- if ($fullItem['type'] == ITEM_TYPE_AGGREGATE && $fullItem['value_type'] != ITEM_VALUE_TYPE_UINT64
- && $fullItem['value_type'] != ITEM_VALUE_TYPE_FLOAT) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Type of information must be "Numeric (unsigned)" or "Numeric (float)" for aggregate items.'));
- }
-
if (($fullItem['type'] == ITEM_TYPE_TRAPPER || $fullItem['type'] == ITEM_TYPE_HTTPAGENT)
&& array_key_exists('trapper_hosts', $fullItem) && $fullItem['trapper_hosts'] !== ''
&& !$ip_range_parser->parse($fullItem['trapper_hosts'])) {
diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php
index f142d5c9ea1..ba2db865c2e 100644
--- a/ui/include/classes/api/services/CTriggerGeneral.php
+++ b/ui/include/classes/api/services/CTriggerGeneral.php
@@ -111,8 +111,8 @@ abstract class CTriggerGeneral extends CApiService {
$this->getHostTriggersByDescription($tpl_triggers_by_description)
);
- $expression_data = new CTriggerExpression(['lldmacros' => $this instanceof CTriggerPrototype]);
- $recovery_expression_data = new CTriggerExpression(['lldmacros' => $this instanceof CTriggerPrototype]);
+ $expression_parser = new CExpressionParser(['lldmacros' => $this instanceof CTriggerPrototype]);
+ $recovery_expression_parser = new CExpressionParser(['lldmacros' => $this instanceof CTriggerPrototype]);
// List of triggers to check for duplicates. Grouped by description.
$descriptions = [];
@@ -136,15 +136,19 @@ abstract class CTriggerGeneral extends CApiService {
$tpl_hostid = $tpl_hostids_by_triggerid[$tpl_trigger['triggerid']][0];
- // expression: {template:item.func()} => {host:item.func()}
- if (!$expression_data->parse($tpl_trigger['expression'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $expression_data->error);
+ // expression: func(/template/item) => func(/host/item)
+ if ($expression_parser->parse($tpl_trigger['expression']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'expression', $expression_parser->getError()
+ ));
}
- // recovery_expression: {template:item.func()} => {host:item.func()}
+ // recovery_expression: func(/template/item) => func(/host/item)
if ($tpl_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- if (!$recovery_expression_data->parse($tpl_trigger['recovery_expression'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $recovery_expression_data->error);
+ if ($recovery_expression_parser->parse($tpl_trigger['recovery_expression']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'recovery_expression', $recovery_expression_parser->getError()
+ ));
}
}
@@ -154,25 +158,33 @@ abstract class CTriggerGeneral extends CApiService {
if (array_key_exists($tpl_hostid, $hosts_by_tpl_hostid)) {
foreach ($hosts_by_tpl_hostid[$tpl_hostid] as $host) {
$new_trigger['expression'] = $tpl_trigger['expression'];
- $expr_part = end($expression_data->expressions);
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
+ $query_parameter = $hist_function['data']['parameters'][0];
$new_trigger['expression'] = substr_replace($new_trigger['expression'],
- '{'.$host['host'].':'.$expr_part['item'].'.'.$expr_part['function'].'}',
- $expr_part['pos'], strlen($expr_part['expression'])
+ '/'.$host['host'].'/'.$query_parameter['data']['item'], $query_parameter['pos'],
+ $query_parameter['length']
);
}
- while ($expr_part = prev($expression_data->expressions));
+ while ($hist_function = prev($hist_functions));
if ($tpl_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
$new_trigger['recovery_expression'] = $tpl_trigger['recovery_expression'];
- $expr_part = end($recovery_expression_data->expressions);
+ $hist_functions = $recovery_expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
+ $query_parameter = $hist_function['data']['parameters'][0];
$new_trigger['recovery_expression'] = substr_replace($new_trigger['recovery_expression'],
- '{'.$host['host'].':'.$expr_part['item'].'.'.$expr_part['function'].'}',
- $expr_part['pos'], strlen($expr_part['expression'])
+ '/'.$host['host'].'/'.$query_parameter['data']['item'], $query_parameter['pos'],
+ $query_parameter['length']
);
}
- while ($expr_part = prev($recovery_expression_data->expressions));
+ while ($hist_function = prev($hist_functions));
}
if (array_key_exists($host['hostid'], $chd_triggers_all)
@@ -373,8 +385,8 @@ abstract class CTriggerGeneral extends CApiService {
private function getHostTriggersByDescription(array $tpl_triggers_by_description) {
$chd_triggers_description = [];
- $expression_data = new CTriggerExpression(['lldmacros' => $this instanceof CTriggerPrototype]);
- $recovery_expression_data = new CTriggerExpression(['lldmacros' => $this instanceof CTriggerPrototype]);
+ $expression_parser = new CExpressionParser(['lldmacros' => $this instanceof CTriggerPrototype]);
+ $recovery_expression_parser = new CExpressionParser(['lldmacros' => $this instanceof CTriggerPrototype]);
$output = 't.triggerid,t.expression,t.description,t.url,t.status,t.priority,t.comments,t.type,t.recovery_mode,'.
't.recovery_expression,t.correlation_mode,t.correlation_tag,t.manual_close,t.opdata,t.event_name,i.hostid,'.
@@ -405,15 +417,20 @@ abstract class CTriggerGeneral extends CApiService {
);
foreach ($tpl_triggers as $tpl_trigger) {
- // expression: {template:item.func()} => {host:item.func()}
- if (!$expression_data->parse($tpl_trigger['expression'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $expression_data->error);
+ // expression: func(/template/item) => func(/host/item)
+ if ($expression_parser->parse($tpl_trigger['expression']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'expression', $expression_parser->getError()
+ ));
}
- // recovery_expression: {template:item.func()} => {host:item.func()}
+ // recovery_expression: func(/template/item) => func(/host/item)
if ($tpl_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- if (!$recovery_expression_data->parse($tpl_trigger['recovery_expression'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $recovery_expression_data->error);
+ if ($recovery_expression_parser->parse($tpl_trigger['recovery_expression']) !=
+ CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'recovery_expression', $recovery_expression_parser->getError()
+ ));
}
}
@@ -426,15 +443,20 @@ abstract class CTriggerGeneral extends CApiService {
continue;
}
+ // Replace template name in /host/key reference to target host name.
$expression = $tpl_trigger['expression'];
- $expr_part = end($expression_data->expressions);
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
+ $query_parameter = $hist_function['data']['parameters'][0];
$expression = substr_replace($expression,
- '{'.$chd_trigger['host'].':'.$expr_part['item'].'.'.$expr_part['function'].'}',
- $expr_part['pos'], strlen($expr_part['expression'])
+ '/'.$chd_trigger['host'].'/'.$query_parameter['data']['item'], $query_parameter['pos'],
+ $query_parameter['length']
);
}
- while ($expr_part = prev($expression_data->expressions));
+ while ($hist_function = prev($hist_functions));
if ($chd_trigger['expression'] !== $expression) {
continue;
@@ -442,14 +464,18 @@ abstract class CTriggerGeneral extends CApiService {
if ($tpl_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
$recovery_expression = $tpl_trigger['recovery_expression'];
- $expr_part = end($recovery_expression_data->expressions);
+ $hist_functions = $recovery_expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
+ $query_parameter = $hist_function['data']['parameters'][0];
$recovery_expression = substr_replace($recovery_expression,
- '{'.$chd_trigger['host'].':'.$expr_part['item'].'.'.$expr_part['function'].'}',
- $expr_part['pos'], strlen($expr_part['expression'])
+ '/'.$chd_trigger['host'].'/'.$query_parameter['data']['item'], $query_parameter['pos'],
+ $query_parameter['length']
);
}
- while ($expr_part = prev($recovery_expression_data->expressions));
+ while ($hist_function = prev($hist_functions));
if ($chd_trigger['recovery_expression'] !== $recovery_expression) {
continue;
@@ -506,14 +532,14 @@ abstract class CTriggerGeneral extends CApiService {
* @return array
*/
protected function populateHostIds($descriptions) {
- $expression_data = new CTriggerExpression(['lldmacros' => $this instanceof CTriggerPrototype]);
+ $expression_parser = new CExpressionParser(['lldmacros' => $this instanceof CTriggerPrototype]);
$hosts = [];
foreach ($descriptions as $description => $triggers) {
foreach ($triggers as $index => $trigger) {
- $expression_data->parse($trigger['expression']);
- $hosts[$expression_data->getHosts()[0]][$description][] = $index;
+ $expression_parser->parse($trigger['expression']);
+ $hosts[$expression_parser->getResult()->getHosts()[0]][$description][] = $index;
}
}
@@ -1360,8 +1386,7 @@ abstract class CTriggerGeneral extends CApiService {
* Implodes expression and recovery_expression for each trigger. Also returns array of functions and
* array of hostnames for each trigger.
*
- * For example: {localhost:system.cpu.load.last(0)}>10 will be translated to {12}>10 and
- * created database representation.
+ * For example: last(/host/system.cpu.load)>10 will be translated to {12}>10 and created database representation.
*
* Note: All expressions must be already validated and exploded.
*
@@ -1393,23 +1418,23 @@ abstract class CTriggerGeneral extends CApiService {
switch ($class) {
case 'CTrigger':
- $expressionData = new CTriggerExpression(['lldmacros' => false]);
+ $expression_parser = new CExpressionParser();
$error_wrong_host = _('Incorrect trigger expression. Host "%1$s" does not exist or you have no access to this host.');
$error_host_and_template = _('Incorrect trigger expression. Trigger expression elements should not belong to a template and a host simultaneously.');
- $triggerFunctionValidator = new CFunctionValidator(['lldmacros' => false]);
break;
case 'CTriggerPrototype':
- $expressionData = new CTriggerExpression();
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
$error_wrong_host = _('Incorrect trigger prototype expression. Host "%1$s" does not exist or you have no access to this host.');
$error_host_and_template = _('Incorrect trigger prototype expression. Trigger prototype expression elements should not belong to a template and a host simultaneously.');
- $triggerFunctionValidator = new CFunctionValidator();
break;
default:
self::exception(ZBX_API_ERROR_INTERNAL, _('Internal error.'));
}
+ $hist_function_value_types = (new CHistFunctionData())->getValueTypes();
+
/*
* [
* <host> => [
@@ -1434,33 +1459,40 @@ abstract class CTriggerGeneral extends CApiService {
foreach ($triggers as $tnum => $trigger) {
$expressions_changed = ($db_triggers === null
|| ($trigger['expression'] !== $db_triggers[$tnum]['expression']
- || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']));
+ || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']));
if (!$expressions_changed) {
continue;
}
- $expressionData->parse($trigger['expression']);
- $expressions = $expressionData->expressions;
+ $expression_parser->parse($trigger['expression']);
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- $expressionData->parse($trigger['recovery_expression']);
- $expressions = array_merge($expressions, $expressionData->expressions);
+ $expression_parser->parse($trigger['recovery_expression']);
+ $hist_functions = array_merge($hist_functions, $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ ));
}
- foreach ($expressions as $exprPart) {
- if (!array_key_exists($exprPart['host'], $hosts_keys)) {
- $hosts_keys[$exprPart['host']] = [
+ foreach ($hist_functions as $hist_function) {
+ $host = $hist_function['data']['parameters'][0]['data']['host'];
+ $item = $hist_function['data']['parameters'][0]['data']['item'];
+
+ if (!array_key_exists($host, $hosts_keys)) {
+ $hosts_keys[$host] = [
'hostid' => null,
- 'host' => $exprPart['host'],
+ 'host' => $host,
'status' => null,
'keys' => []
];
}
- $hosts_keys[$exprPart['host']]['keys'][$exprPart['item']] = [
+ $hosts_keys[$host]['keys'][$item] = [
'itemid' => null,
- 'key' => $exprPart['item'],
+ 'key' => $item,
'value_type' => null,
'flags' => null
];
@@ -1562,21 +1594,28 @@ abstract class CTriggerGeneral extends CApiService {
}
foreach ($triggers as $tnum => &$trigger) {
- $expressions_changed = $db_triggers === null
+ $expressions_changed = ($db_triggers === null
|| ($trigger['expression'] !== $db_triggers[$tnum]['expression']
- || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']);
+ || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']));
if (!$expressions_changed) {
continue;
}
- $expressionData->parse($trigger['expression']);
- $expressions1 = $expressionData->expressions;
- $expressions2 = [];
+ $expression_parser->parse($trigger['expression']);
+
+ $hist_functions1 = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+
+ $hist_functions2 = [];
if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- $expressionData->parse($trigger['recovery_expression']);
- $expressions2 = $expressionData->expressions;
+ $expression_parser->parse($trigger['recovery_expression']);
+
+ $hist_functions2 = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
}
$triggers_functions[$tnum] = [];
@@ -1594,9 +1633,12 @@ abstract class CTriggerGeneral extends CApiService {
$hosts = [];
// Common checks.
- foreach (array_merge($expressions1, $expressions2) as $exprPart) {
- $host_keys = $hosts_keys[$exprPart['host']];
- $key = $host_keys['keys'][$exprPart['item']];
+ foreach (array_merge($hist_functions1, $hist_functions2) as $hist_function) {
+ $host = $hist_function['data']['parameters'][0]['data']['host'];
+ $item = $hist_function['data']['parameters'][0]['data']['item'];
+
+ $host_keys = $hosts_keys[$host];
+ $key = $host_keys['keys'][$item];
if ($host_keys['hostid'] === null) {
self::exception(ZBX_API_ERROR_PARAMETERS, _params($error_wrong_host, [$host_keys['host']]));
@@ -1609,21 +1651,24 @@ abstract class CTriggerGeneral extends CApiService {
));
}
- if (!$triggerFunctionValidator->validate([
- 'function' => $exprPart['function'],
- 'functionName' => $exprPart['functionName'],
- 'functionParamList' => $exprPart['functionParamList'],
- 'valueType' => $key['value_type']])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $triggerFunctionValidator->getError());
+ if (!in_array($key['value_type'], $hist_function_value_types[$hist_function['data']['function']])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Incorrect item value type "%1$s" provided for trigger function "%2$s".',
+ itemValueTypeString($key['value_type']), $hist_function['data']['function']
+ ));
}
- if (!array_key_exists($exprPart['expression'], $triggers_functions[$tnum])) {
- $triggers_functions[$tnum][$exprPart['expression']] = [
+ if (!array_key_exists($hist_function['match'], $triggers_functions[$tnum])) {
+ $query_parameter = $hist_function['data']['parameters'][0];
+ $parameter = substr_replace($hist_function['match'], TRIGGER_QUERY_PLACEHOLDER,
+ $query_parameter['pos'] - $hist_function['pos'], $query_parameter['length']
+ );
+ $triggers_functions[$tnum][$hist_function['match']] = [
'functionid' => null,
'triggerid' => null,
'itemid' => $key['itemid'],
- 'name' => $exprPart['functionName'],
- 'parameter' => $exprPart['functionParam']
+ 'name' => $hist_function['data']['function'],
+ 'parameter' => substr($parameter, strlen($hist_function['data']['function']) + 1, -1)
];
$functions_num++;
}
@@ -1634,7 +1679,7 @@ abstract class CTriggerGeneral extends CApiService {
$status_mask |= ($host_keys['status'] == HOST_STATUS_TEMPLATE ? 0x01 : 0x02);
$hostids[$host_keys['hostid']] = true;
- $hosts[$exprPart['host']] = true;
+ $hosts[$host] = true;
}
// When both templates and hosts are referenced in expressions.
@@ -1644,13 +1689,13 @@ abstract class CTriggerGeneral extends CApiService {
// Triggers with children cannot be moved from one template to another host or template.
if ($class === 'CTrigger' && $db_triggers !== null && $expressions_changed) {
- $expressionData->parse($db_triggers[$tnum]['expression']);
- $old_hosts1 = $expressionData->getHosts();
+ $expression_parser->parse($db_triggers[$tnum]['expression']);
+ $old_hosts1 = $expression_parser->getResult()->getHosts();
$old_hosts2 = [];
if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- $expressionData->parse($db_triggers[$tnum]['recovery_expression']);
- $old_hosts2 = $expressionData->getHosts();
+ $expression_parser->parse($db_triggers[$tnum]['recovery_expression']);
+ $old_hosts2 = $expression_parser->getResult()->getHosts();
}
$is_moved = true;
@@ -1711,11 +1756,11 @@ abstract class CTriggerGeneral extends CApiService {
$expression_max_length = DB::getFieldLength('triggers', 'expression');
$recovery_expression_max_length = DB::getFieldLength('triggers', 'recovery_expression');
- // Replace {host:item.func()} macros with {<functionid>}.
+ // Replace func(/host/item) macros with {<functionid>}.
foreach ($triggers as $tnum => &$trigger) {
- $expressions_changed = $db_triggers === null
+ $expressions_changed = ($db_triggers === null
|| ($trigger['expression'] !== $db_triggers[$tnum]['expression']
- || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']);
+ || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']));
if (!$expressions_changed) {
continue;
@@ -1725,17 +1770,20 @@ abstract class CTriggerGeneral extends CApiService {
$trigger_function['functionid'] = $functionid;
$functionid = bcadd($functionid, 1, 0);
}
- unset($function);
+ unset($trigger_function);
- $expressionData->parse($trigger['expression']);
- $exprPart = end($expressionData->expressions);
+ $expression_parser->parse($trigger['expression']);
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
$trigger['expression'] = substr_replace($trigger['expression'],
- '{'.$triggers_functions[$tnum][$exprPart['expression']]['functionid'].'}',
- $exprPart['pos'], strlen($exprPart['expression'])
+ '{'.$triggers_functions[$tnum][$hist_function['match']]['functionid'].'}',
+ $hist_function['pos'], $hist_function['length']
);
}
- while ($exprPart = prev($expressionData->expressions));
+ while ($hist_function = prev($hist_functions));
if (mb_strlen($trigger['expression']) > $expression_max_length) {
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
@@ -1744,15 +1792,18 @@ abstract class CTriggerGeneral extends CApiService {
}
if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) {
- $expressionData->parse($trigger['recovery_expression']);
- $exprPart = end($expressionData->expressions);
+ $expression_parser->parse($trigger['recovery_expression']);
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
do {
$trigger['recovery_expression'] = substr_replace($trigger['recovery_expression'],
- '{'.$triggers_functions[$tnum][$exprPart['expression']]['functionid'].'}',
- $exprPart['pos'], strlen($exprPart['expression'])
+ '{'.$triggers_functions[$tnum][$hist_function['match']]['functionid'].'}',
+ $hist_function['pos'], $hist_function['length']
);
}
- while ($exprPart = prev($expressionData->expressions));
+ while ($hist_function = prev($hist_functions));
if (mb_strlen($trigger['recovery_expression']) > $recovery_expression_max_length) {
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
diff --git a/ui/include/classes/core/ZBase.php b/ui/include/classes/core/ZBase.php
index 4154229587e..dc379f2c907 100644
--- a/ui/include/classes/core/ZBase.php
+++ b/ui/include/classes/core/ZBase.php
@@ -296,6 +296,7 @@ class ZBase {
$this->rootDir.'/include/classes/api/clients',
$this->rootDir.'/include/classes/api/wrappers',
$this->rootDir.'/include/classes/core',
+ $this->rootDir.'/include/classes/data',
$this->rootDir.'/include/classes/mvc',
$this->rootDir.'/include/classes/db',
$this->rootDir.'/include/classes/debug',
diff --git a/ui/include/classes/data/CHistFunctionData.php b/ui/include/classes/data/CHistFunctionData.php
new file mode 100644
index 00000000000..150c7f98e02
--- /dev/null
+++ b/ui/include/classes/data/CHistFunctionData.php
@@ -0,0 +1,317 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class containing information on history functions.
+ */
+final class CHistFunctionData {
+
+ public const PERIOD_MODE_DEFAULT = 0;
+ public const PERIOD_MODE_SEC = 1;
+ public const PERIOD_MODE_NUM = 2;
+ public const PERIOD_MODE_TREND = 3;
+
+ /**
+ * Known history functions along with definition of parameters.
+ *
+ * @var array
+ */
+ private const PARAMETERS = [
+ 'avg' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'avg_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
+ 'count' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
+ ['rules' => [['type' => 'regexp', 'pattern' => '/^(eq|ne|gt|ge|lt|le|like|bitand|regexp|iregexp)$/']],
+ 'required' => false
+ ],
+ ['required' => false]
+ ],
+ 'count_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
+ 'change' => [
+ ['rules' => [['type' => 'query']]]
+ ],
+ 'find' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]], 'required' => false],
+ ['rules' => [['type' => 'regexp', 'pattern' => '/^(eq|ne|gt|ge|lt|le|like|bitand|regexp|iregexp)$/']],
+ 'required' => false
+ ],
+ ['required' => false]
+ ],
+ 'forecast' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
+ ['rules' => [['type' => 'time']]],
+ ['rules' => [
+ ['type' => 'regexp', 'pattern' => '/^(linear|polynomial[1-6]|exponential|logarithmic|power)$/']
+ ], 'required' => false],
+ ['rules' => [['type' => 'regexp', 'pattern' => '/^(value|max|min|delta|avg)$/']], 'required' => false]
+ ],
+ 'fuzzytime' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'time', 'min' => 1]]]
+ ],
+ 'last' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false]
+ ],
+ 'last_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]], 'required' => false]
+ ],
+ 'logeventid' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false],
+ ['required' => false]
+ ],
+ 'logseverity' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false]
+ ],
+ 'logsource' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false],
+ ['required' => false]
+ ],
+ 'max' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'max_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
+ 'min' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'min_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
+ 'nodata' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'time', 'min' => 1]]],
+ ['rules' => [['type' => 'regexp', 'pattern' => '/^(strict)$/']], 'required' => false]
+ ],
+ 'percentile' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
+ ['rules' => [
+ ['type' => 'regexp', 'pattern' => '/^((\d+(\.\d{0,4})?)|(\.\d{1,4}))$/'],
+ ['type' => 'number', 'min' => 0, 'max' => 100]
+ ]]
+ ],
+ 'sum' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'sum_foreach' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
+ 'timeleft' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
+ ['rules' => [['type' => 'number', 'with_suffix' => true]]],
+ ['rules' => [
+ ['type' => 'regexp', 'pattern' => '/^(linear|polynomial[1-6]|exponential|logarithmic|power)$/']
+ ], 'required' => false]
+ ],
+ 'trendavg' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_TREND]]]
+ ],
+ 'trendcount' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_TREND]]]
+ ],
+ 'trendmax' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_TREND]]]
+ ],
+ 'trendmin' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_TREND]]]
+ ],
+ 'trendsum' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_TREND]]]
+ ]
+ ];
+
+ private const ITEM_VALUE_TYPES_INT = [ITEM_VALUE_TYPE_UINT64];
+ private const ITEM_VALUE_TYPES_NUM = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64];
+ private const ITEM_VALUE_TYPES_STR = [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_TEXT, ITEM_VALUE_TYPE_LOG];
+ private const ITEM_VALUE_TYPES_LOG = [ITEM_VALUE_TYPE_LOG];
+ private const ITEM_VALUE_TYPES_ALL = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_STR,
+ ITEM_VALUE_TYPE_TEXT, ITEM_VALUE_TYPE_LOG
+ ];
+
+ /**
+ * Known history functions along with supported item value types.
+ *
+ * @var array
+ */
+ private const VALUE_TYPES = [
+ 'avg' => self::ITEM_VALUE_TYPES_NUM,
+ 'avg_foreach' => self::ITEM_VALUE_TYPES_NUM,
+ 'count' => self::ITEM_VALUE_TYPES_ALL,
+ 'count_foreach' => self::ITEM_VALUE_TYPES_ALL,
+ 'change' => self::ITEM_VALUE_TYPES_ALL,
+ 'find' => self::ITEM_VALUE_TYPES_ALL,
+ 'forecast' => self::ITEM_VALUE_TYPES_NUM,
+ 'fuzzytime' => self::ITEM_VALUE_TYPES_NUM,
+ 'last' => self::ITEM_VALUE_TYPES_ALL,
+ 'last_foreach' => self::ITEM_VALUE_TYPES_ALL,
+ 'logeventid' => self::ITEM_VALUE_TYPES_LOG,
+ 'logseverity' => self::ITEM_VALUE_TYPES_LOG,
+ 'logsource' => self::ITEM_VALUE_TYPES_LOG,
+ 'max' => self::ITEM_VALUE_TYPES_NUM,
+ 'max_foreach' => self::ITEM_VALUE_TYPES_NUM,
+ 'min' => self::ITEM_VALUE_TYPES_NUM,
+ 'min_foreach' => self::ITEM_VALUE_TYPES_NUM,
+ 'nodata' => self::ITEM_VALUE_TYPES_ALL,
+ 'percentile' => self::ITEM_VALUE_TYPES_NUM,
+ 'sum' => self::ITEM_VALUE_TYPES_NUM,
+ 'sum_foreach' => self::ITEM_VALUE_TYPES_NUM,
+ 'timeleft' => self::ITEM_VALUE_TYPES_NUM,
+ 'trendavg' => self::ITEM_VALUE_TYPES_NUM,
+ 'trendcount' => self::ITEM_VALUE_TYPES_NUM,
+ 'trendmax' => self::ITEM_VALUE_TYPES_NUM,
+ 'trendmin' => self::ITEM_VALUE_TYPES_NUM,
+ 'trendsum' => self::ITEM_VALUE_TYPES_NUM
+ ];
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'calculated' => false Provide history functions data for use in calculated item formulas.
+ *
+ * @var array
+ */
+ private $options = [
+ 'calculated' => false
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+ }
+
+ /**
+ * Check if function is known history function.
+ *
+ * @param string $function
+ *
+ * @return bool
+ */
+ public function isKnownFunction(string $function): bool {
+ if (!array_key_exists($function, self::PARAMETERS)) {
+ return false;
+ }
+
+ return ($this->options['calculated'] || !self::isAggregating($function));
+ }
+
+ /**
+ * Get known history functions along with definition of parameters.
+ *
+ * @return array
+ */
+ public function getParameters(): array {
+ if ($this->options['calculated']) {
+ return self::PARAMETERS;
+ }
+
+ $result = [];
+
+ foreach (self::PARAMETERS as $function => $parameters) {
+ if (self::isAggregating($function)) {
+ continue;
+ }
+
+ $result[$function] = $parameters;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get known history functions along with supported item value types.
+ *
+ * @return array
+ */
+ public function getValueTypes(): array {
+ if ($this->options['calculated']) {
+ return self::VALUE_TYPES;
+ }
+
+ $result = [];
+
+ foreach (self::VALUE_TYPES as $function => $value_types) {
+ if (self::isAggregating($function)) {
+ continue;
+ }
+
+ $result[$function] = $value_types;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Check if function is aggregating wildcarded host/item queries and is exclusive to calculated item formulas.
+ *
+ * @static
+ *
+ * @param string $function
+ *
+ * @return bool
+ */
+ public static function isAggregating(string $function): bool {
+ switch ($function) {
+ case 'avg_foreach':
+ case 'count_foreach':
+ case 'last_foreach':
+ case 'max_foreach':
+ case 'min_foreach':
+ case 'sum_foreach':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
diff --git a/ui/include/classes/data/CMathFunctionData.php b/ui/include/classes/data/CMathFunctionData.php
new file mode 100644
index 00000000000..8d1b806c840
--- /dev/null
+++ b/ui/include/classes/data/CMathFunctionData.php
@@ -0,0 +1,88 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class containing information on math functions.
+ */
+final class CMathFunctionData {
+
+ /**
+ * Known math functions along with number of required parameters (-1 for number of required parameters >= 1).
+ *
+ * @var array
+ */
+ private const PARAMETERS = [
+ 'abs' => 1,
+ 'avg' => -1,
+ 'bitand' => 2,
+ 'date' => 0,
+ 'dayofmonth' => 0,
+ 'dayofweek' => 0,
+ 'length' => 1,
+ 'max' => -1,
+ 'min' => -1,
+ 'now' => 0,
+ 'sum' => -1,
+ 'time' => 0
+ ];
+
+ /**
+ * Check if function is known math function.
+ *
+ * @param string $function
+ *
+ * @return bool
+ */
+ public function isKnownFunction(string $function): bool {
+ return array_key_exists($function, self::PARAMETERS);
+ }
+
+ /**
+ * Get known math functions along with number of required parameters (-1 for number of required parameters >= 1).
+ *
+ * @return array
+ */
+ public function getParameters(): array {
+ return self::PARAMETERS;
+ }
+
+ /**
+ * Check if function is aggregating it's parameters or the result of aggregating history functions.
+ *
+ * @static
+ *
+ * @param string $function
+ *
+ * @return bool
+ */
+ public static function isAggregating(string $function): bool {
+ switch ($function) {
+ case 'avg':
+ case 'max':
+ case 'min':
+ case 'sum':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
diff --git a/ui/include/classes/export/CConfigurationExport.php b/ui/include/classes/export/CConfigurationExport.php
index 9dd530ac725..d1efffabefc 100644
--- a/ui/include/classes/export/CConfigurationExport.php
+++ b/ui/include/classes/export/CConfigurationExport.php
@@ -1118,22 +1118,6 @@ class CConfigurationExport {
}
/**
- * Get value maps for export builder from database.
- *
- * @param array $valuemapids
- *
- * return array
- */
- protected function gatherValueMaps(array $valuemapids) {
- $this->data['valueMaps'] = API::ValueMap()->get([
- 'output' => ['valuemapid', 'name'],
- 'selectMappings' => ['value', 'newvalue'],
- 'valuemapids' => $valuemapids,
- 'preservekeys' => true
- ]);
- }
-
- /**
* Change map elements real database selement id and icons ids to unique field references.
*
* @param array $exportMaps
diff --git a/ui/include/classes/export/CConfigurationExportBuilder.php b/ui/include/classes/export/CConfigurationExportBuilder.php
index 212af90d660..b436d2324ca 100644
--- a/ui/include/classes/export/CConfigurationExportBuilder.php
+++ b/ui/include/classes/export/CConfigurationExportBuilder.php
@@ -989,7 +989,6 @@ class CConfigurationExportBuilder {
*/
protected function formatItems(array $items, array $simple_triggers) {
$result = [];
- $expression_data = $simple_triggers ? new CTriggerExpression() : null;
CArrayHelper::sort($items, ['key_']);
@@ -1079,32 +1078,8 @@ class CConfigurationExportBuilder {
if ($simple_triggers) {
$triggers = [];
- $prefix_length = strlen($item['host'].':'.$item['key_'].'.');
-
foreach ($simple_triggers as $simple_trigger) {
if (bccomp($item['itemid'], $simple_trigger['items'][0]['itemid']) == 0) {
- if ($expression_data->parse($simple_trigger['expression'])) {
- foreach (array_reverse($expression_data->expressions) as $expression) {
- if ($expression['host'] === $item['host'] && $expression['item'] === $item['key_']) {
- $simple_trigger['expression'] = substr_replace($simple_trigger['expression'], '',
- $expression['pos'] + 1, $prefix_length
- );
- }
- }
- }
-
- if ($simple_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION
- && $expression_data->parse($simple_trigger['recovery_expression'])) {
- foreach (array_reverse($expression_data->expressions) as $expression) {
- if ($expression['host'] === $item['host'] && $expression['item'] === $item['key_']) {
- $simple_trigger['recovery_expression'] = substr_replace(
- $simple_trigger['recovery_expression'], '', $expression['pos'] + 1,
- $prefix_length
- );
- }
- }
- }
-
$triggers[] = $simple_trigger;
}
}
diff --git a/ui/include/classes/export/writers/CXmlExportWriter.php b/ui/include/classes/export/writers/CXmlExportWriter.php
index 27a37d515ff..c43c9ef00a2 100644
--- a/ui/include/classes/export/writers/CXmlExportWriter.php
+++ b/ui/include/classes/export/writers/CXmlExportWriter.php
@@ -124,7 +124,6 @@ class CXmlExportWriter extends CExportWriter {
'overrides' => 'override',
'pages' => 'page',
'parameters' => 'parameter',
- 'parameters' => 'parameter',
'posts' => 'post_field',
'preprocessing' => 'step',
'query_fields' => 'query_field',
diff --git a/ui/include/classes/import/CConfigurationImport.php b/ui/include/classes/import/CConfigurationImport.php
index 9e0d001a670..bfa67d7c448 100644
--- a/ui/include/classes/import/CConfigurationImport.php
+++ b/ui/include/classes/import/CConfigurationImport.php
@@ -103,7 +103,7 @@ class CConfigurationImport {
|| $options['httptests']['createMissing']
|| $options['httptests']['deleteMissing']
);
- // TODO VM: why not 'templateLinkage' ?
+
$options['process_templates'] = (
!$options['templates']['updateExisting']
&& ($object_options
@@ -334,8 +334,6 @@ class CConfigurationImport {
]
: [];
- // TODO VM: test, how group prototypes are managed in UUID context.
- // TODO VM: why there is no 'group_links', and how common groups can be in 'group_prototypes'?
foreach ($host_prototype['group_prototypes'] as $group_prototype) {
if (isset($group_prototype['group'])) {
$groups_refs += [$group_prototype['group']['name'] => []];
@@ -1627,9 +1625,13 @@ class CConfigurationImport {
}
unset($item);
- $graphid = (array_key_exists('uuid', $graph))
- ? $this->referencer->findGraphidByUuid($graph['uuid'])
- : $this->referencer->findGraphidByName($hostid, $graph['name']);
+ if ($this->isTemplateGraph($graph)) {
+ $graphid = $this->referencer->findGraphidByUuid($graph['uuid']);
+ }
+ else {
+ unset($graph['uuid']);
+ $graphid = $this->referencer->findGraphidByName($hostid, $graph['name']);
+ }
if ($graphid !== null) {
$graph['graphid'] = $graphid;
@@ -1652,6 +1654,26 @@ class CConfigurationImport {
$this->referencer->refreshGraphs();
}
+ private function isTemplateGraph(array $graph): bool {
+ if ($graph['ymin_item_1'] && $this->referencer->findTemplateidByHost($graph['ymin_item_1']['host'])) {
+ return true;
+ }
+
+ if ($graph['ymax_item_1'] && $this->referencer->findTemplateidByHost($graph['ymax_item_1']['host'])) {
+ return true;
+ }
+
+ if (array_key_exists('gitems', $graph) && $graph['gitems']) {
+ foreach ($graph['gitems'] as $gitem) {
+ if ($this->referencer->findTemplateidByHost($gitem['item']['host'])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
/**
* Import triggers.
*
@@ -1670,24 +1692,16 @@ class CConfigurationImport {
foreach ($this->getFormattedTriggers() as $trigger) {
$triggerid = null;
- if (array_key_exists('uuid', $trigger)) {
+ $is_template_trigger = $this->isTemplateTrigger($trigger);
+
+ if ($is_template_trigger && array_key_exists('uuid', $trigger)) {
$triggerid = $this->referencer->findTriggeridByUuid($trigger['uuid']);
}
-
- // In import file host trigger can have UUID assigned after conversion, such should be searched by name.
- if ($triggerid === null) {
+ elseif (!$is_template_trigger) {
+ unset($trigger['uuid']);
$triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'],
$trigger['recovery_expression']
);
-
- // Template triggers should only be searched by UUID.
- if ($triggerid !== null && array_key_exists('uuid', $trigger)) {
- $db_trigger = $this->referencer->findTriggerById($triggerid);
-
- if ($db_trigger['uuid'] !== '' && $db_trigger['uuid'] !== $trigger['uuid']) {
- $triggerid = null;
- }
- }
}
if ($triggerid !== null) {
@@ -1723,6 +1737,37 @@ class CConfigurationImport {
$this->processTriggerDependencies($triggers_to_process_dependencies);
}
+ private function isTemplateTrigger(array $trigger): bool {
+ $expression_parser = new CExpressionParser();
+
+ if ($expression_parser->parse($trigger['expression']) != CParser::PARSE_SUCCESS) {
+ return false;
+ }
+
+ foreach ($expression_parser->getResult()->getHosts() as $host) {
+ $host = $this->referencer->findTemplateidByHost($host);
+
+ if ($host !== null) {
+ return true;
+ }
+ }
+
+ if ($trigger['recovery_expression'] === ''
+ || $expression_parser->parse($trigger['recovery_expression']) != CParser::PARSE_SUCCESS) {
+ return false;
+ }
+
+ foreach ($expression_parser->getResult()->getHosts() as $host) {
+ $host = $this->referencer->findTemplateidByHost($host);
+
+ if ($host !== null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Update trigger dependencies
*
diff --git a/ui/include/classes/import/CConfigurationImportcompare.php b/ui/include/classes/import/CConfigurationImportcompare.php
index 4a6d4a7e185..674b86ea2ec 100644
--- a/ui/include/classes/import/CConfigurationImportcompare.php
+++ b/ui/include/classes/import/CConfigurationImportcompare.php
@@ -227,7 +227,6 @@ class CConfigurationImportcompare {
'dashboards' => 'templateDashboards',
'httptests' => 'httptests',
'valuemaps' => 'valueMaps',
- 'triggers' => 'triggers',
'graphs' => 'graphs'
];
@@ -235,7 +234,8 @@ class CConfigurationImportcompare {
$stored_changes = [];
if ($entity_key === 'templates' && array_key_exists('updated', $diff)) {
- for ($key = 0; $key< count($diff['updated']); $key++) {
+ $updated_count = count($diff['updated']);
+ for ($key = 0; $key < $updated_count; $key++) {
$entity = $diff['updated'][$key];
$has_before_templates = array_key_exists('templates', $entity['before']);
$has_after_templates = array_key_exists('templates', $entity['after']);
diff --git a/ui/include/classes/import/CImportDataAdapter.php b/ui/include/classes/import/CImportDataAdapter.php
index 26ee380545a..de53c979d3f 100644
--- a/ui/include/classes/import/CImportDataAdapter.php
+++ b/ui/include/classes/import/CImportDataAdapter.php
@@ -261,61 +261,29 @@ class CImportDataAdapter {
}
/**
- * Get simple triggers from the imported data.
+ * Get triggers from the imported data.
*
* @return array
*/
- protected function getSimpleTriggers(): array {
- $simple_triggers = [];
- $expression_options = ['lldmacros' => false, 'allow_func_only' => true];
-
- if (array_key_exists('hosts', $this->data)) {
- foreach ($this->data['hosts'] as $host) {
- if (array_key_exists('items', $host)) {
- foreach ($host['items'] as $item) {
- if (array_key_exists('triggers', $item)) {
- foreach ($item['triggers'] as $simple_trigger) {
- $simple_trigger = $this->enrichSimpleTriggerExpression($host['host'], $item['key'],
- $simple_trigger, $expression_options
- );
- $simple_triggers[] = $this->renameTriggerFields($simple_trigger);
- }
- unset($item['triggers']);
- }
- }
- }
- }
- }
+ public function getTriggers() {
+ $triggers = [];
- if (array_key_exists('templates', $this->data)) {
- foreach ($this->data['templates'] as $template) {
- if (array_key_exists('items', $template)) {
- foreach ($template['items'] as $item) {
- if (array_key_exists('triggers', $item)) {
- foreach ($item['triggers'] as $simple_trigger) {
- $simple_trigger = $this->enrichSimpleTriggerExpression($template['template'],
- $item['key'], $simple_trigger, $expression_options
- );
- $simple_triggers[] = $this->renameTriggerFields($simple_trigger);
+ foreach (['hosts', 'templates'] as $source) {
+ if (array_key_exists($source, $this->data)) {
+ foreach ($this->data[$source] as $host) {
+ if (array_key_exists('items', $host)) {
+ foreach ($host['items'] as $item) {
+ if (array_key_exists('triggers', $item)) {
+ foreach ($item['triggers'] as $trigger) {
+ $triggers[] = $this->renameTriggerFields($trigger);
+ }
}
- unset($item['triggers']);
}
}
}
}
}
- return $simple_triggers;
- }
-
- /**
- * Get triggers from the imported data.
- *
- * @return array
- */
- public function getTriggers(): array {
- $triggers = [];
-
if (array_key_exists('triggers', $this->data)) {
foreach ($this->data['triggers'] as $trigger) {
if (array_key_exists('uuid', $trigger) && $trigger['uuid'] === '') {
@@ -326,7 +294,7 @@ class CImportDataAdapter {
}
}
- return array_merge($triggers, $this->getSimpleTriggers());
+ return $triggers;
}
/**
@@ -427,49 +395,6 @@ class CImportDataAdapter {
}
/**
- * Enriches trigger expression and trigger recovery expression with host:item pair.
- *
- * @param string $host
- * @param string $item_key
- * @param array $simple_trigger
- * @param string $simple_trigger['expression]
- * @param int $simple_trigger['recovery_mode]
- * @param string $simple_trigger['recovery_expression]
- * @param array $options
- * @param bool $options['lldmacros'] (optional)
- * @param bool $options['allow_func_only'] (optional)
- *
- * @return array
- */
- protected function enrichSimpleTriggerExpression($host, $item_key, array $simple_trigger, array $options) {
- $expression_data = new CTriggerExpression($options);
- $prefix = $host.':'.$item_key.'.';
-
- if ($expression_data->parse($simple_trigger['expression'])) {
- foreach (array_reverse($expression_data->expressions) as $expression) {
- if ($expression['host'] === '' && $expression['item'] === '') {
- $simple_trigger['expression'] = substr_replace($simple_trigger['expression'], $prefix,
- $expression['pos'] + 1, 0
- );
- }
- }
- }
-
- if ($simple_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION
- && $expression_data->parse($simple_trigger['recovery_expression'])) {
- foreach (array_reverse($expression_data->expressions) as $expression) {
- if ($expression['host'] === '' && $expression['item'] === '') {
- $simple_trigger['recovery_expression'] = substr_replace($simple_trigger['recovery_expression'],
- $prefix, $expression['pos'] + 1, 0
- );
- }
- }
- }
-
- return $simple_trigger;
- }
-
- /**
* Format discovery rule.
*
* @param array $discovery_rule
@@ -483,12 +408,9 @@ class CImportDataAdapter {
foreach ($discovery_rule['item_prototypes'] as &$item_prototype) {
if (array_key_exists('trigger_prototypes', $item_prototype)) {
- foreach ($item_prototype['trigger_prototypes'] as $trigger_prototype) {
- $discovery_rule['trigger_prototypes'][] = $this->enrichSimpleTriggerExpression($host,
- $item_prototype['key'], $trigger_prototype, ['allow_func_only' => true]
- );
- }
- unset($item_prototype['trigger_prototypes']);
+ $discovery_rule['trigger_prototypes'] = array_merge($discovery_rule['trigger_prototypes'],
+ $item_prototype['trigger_prototypes']
+ );
}
$item_prototype = $this->renameItemFields($item_prototype);
diff --git a/ui/include/classes/import/CImportReferencer.php b/ui/include/classes/import/CImportReferencer.php
index 5bdeab1c279..ff64e31205d 100644
--- a/ui/include/classes/import/CImportReferencer.php
+++ b/ui/include/classes/import/CImportReferencer.php
@@ -1081,9 +1081,6 @@ class CImportReferencer {
$this->db_triggers[$db_trigger['triggerid']] = $db_trigger;
}
}
-
- // TODO VM: How to check, if nonexisting trigger is from a template? Probably it can only be done by parsing the trigger expressions.
- // TODO VM: if such check is done, they (triggers) need to be added to $this->triggersUuidRefs with 'false' to avoid searching them by name.
}
/**
diff --git a/ui/include/classes/import/converters/C10TriggerConverter.php b/ui/include/classes/import/converters/C10TriggerConverter.php
index acb24b2c580..e022a046b77 100644
--- a/ui/include/classes/import/converters/C10TriggerConverter.php
+++ b/ui/include/classes/import/converters/C10TriggerConverter.php
@@ -27,7 +27,7 @@ class C10TriggerConverter extends CConverter {
/**
* A parser for function macros.
*
- * @var CFunctionMacroParser
+ * @var C10FunctionMacroParser
*/
protected $function_macro_parser;
@@ -39,7 +39,7 @@ class C10TriggerConverter extends CConverter {
protected $itemKeyConverter;
public function __construct() {
- $this->function_macro_parser = new CFunctionMacroParser(['18_simple_checks' => true]);
+ $this->function_macro_parser = new C10FunctionMacroParser(['18_simple_checks' => true]);
$this->itemKeyConverter = new C10ItemKeyConverter();
}
diff --git a/ui/include/classes/import/converters/C20TriggerConverter.php b/ui/include/classes/import/converters/C20TriggerConverter.php
index 4767e67ac22..e329beaece2 100644
--- a/ui/include/classes/import/converters/C20TriggerConverter.php
+++ b/ui/include/classes/import/converters/C20TriggerConverter.php
@@ -27,7 +27,7 @@ class C20TriggerConverter extends CConverter {
/**
* A parser for function macros.
*
- * @var CFunctionMacroParser
+ * @var C10FunctionMacroParser
*/
protected $function_macro_parser;
@@ -46,7 +46,7 @@ class C20TriggerConverter extends CConverter {
protected $itemKeyConverter;
public function __construct() {
- $this->function_macro_parser = new CFunctionMacroParser();
+ $this->function_macro_parser = new C10FunctionMacroParser();
$this->lld_macro_parser = new CLLDMacroParser();
$this->itemKeyConverter = new C20ItemKeyConverter();
}
diff --git a/ui/include/classes/import/converters/C52AggregateItemKeyConverter.php b/ui/include/classes/import/converters/C52AggregateItemKeyConverter.php
new file mode 100644
index 00000000000..72766a5b437
--- /dev/null
+++ b/ui/include/classes/import/converters/C52AggregateItemKeyConverter.php
@@ -0,0 +1,90 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Convert aggregate item key to calculated item function.
+ */
+class C52AggregateItemKeyConverter extends CConverter {
+
+ /**
+ * Item key parser instance.
+ *
+ * @var CItemKey
+ */
+ protected $item_key_parser;
+
+ public function __construct() {
+ $this->item_key_parser = new CItemKey();
+ }
+
+ /**
+ * Convert aggregate item key to calculated item syntax.
+ *
+ * @param string $value Item key.
+ *
+ * @return string Converted item key.
+ */
+ public function convert($value) {
+ $this->item_key = '';
+
+ if ($this->item_key_parser->parse($value) != CParser::PARSE_SUCCESS) {
+ return $value;
+ }
+
+ $params = $this->item_key_parser->getParamsRaw();
+ $params = reset($params);
+
+ if (!$params || count($params['parameters']) < 2) {
+ return $value;
+ }
+
+ $params = $params['parameters'];
+ $this->item_key = trim($this->item_key_parser->getParam(1));
+ $func_foreach = $this->item_key_parser->getParam(2).'_foreach';
+ $item_key = '/*/'.$this->item_key;
+ $host_groups = [$params[0]['raw']];
+
+ if ($params[0]['type'] == CItemKey::PARAM_ARRAY) {
+ $host_groups = array_column($params[0]['parameters'], 'raw');
+ }
+
+ foreach ($host_groups as &$host_group) {
+ if ($host_group[0] === '"') {
+ $host_group = substr($host_group, 1, -1);
+ }
+ }
+
+ $host_groups = array_filter($host_groups, 'strlen');
+
+ if ($host_groups) {
+ $item_key .= '?[group="'.implode('" or group="', $host_groups).'"]';
+ }
+
+ $timeperiod = $this->item_key_parser->getParam(3);
+ $new_value = substr($this->item_key_parser->getKey(), 3).'('.$func_foreach.'('.$item_key;
+
+ if ($timeperiod !== null) {
+ $new_value .= ','.trim($timeperiod);
+ }
+
+ return $new_value.'))';
+ }
+}
diff --git a/ui/include/classes/import/converters/C52CalculatedItemConverter.php b/ui/include/classes/import/converters/C52CalculatedItemConverter.php
new file mode 100644
index 00000000000..86a36436fef
--- /dev/null
+++ b/ui/include/classes/import/converters/C52CalculatedItemConverter.php
@@ -0,0 +1,164 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class to convert calculated item keys.
+ */
+class C52CalculatedItemConverter extends C52TriggerExpressionConverter {
+
+ /**
+ * Item key parser.
+ *
+ * @var CItemKey
+ */
+ protected $item_key_parser;
+
+ /**
+ * Function parser.
+ *
+ * @var C10FunctionParser
+ */
+ protected $function_parser;
+
+ public function __construct() {
+ $this->parser = new C10TriggerExpression([
+ 'allow_func_only' => true,
+ 'calculated' => true
+ ]);
+ $this->item_key_parser = new CItemKey();
+ $this->function_parser = new C10FunctionParser();
+ $this->standalone_functions = getStandaloneFunctions();
+ }
+
+ /**
+ * Convert calculated item formula to 5.4 syntax.
+ *
+ * @param string $formula Calculated item formula to convert.
+ *
+ * @return string
+ */
+ public function convert($item) {
+ $expression = preg_replace("/[\\r\\n\\t]/", '', $item['params']);
+
+ if ($this->parser->parse($expression) === false) {
+ return $item;
+ }
+
+ $extra_expressions = [];
+ $functions = $this->parser->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION);
+ $this->hanged_refs = $this->checkHangedFunctionsPerHost($functions);
+ $parts = $this->getExpressionParts(0, $this->parser->result->length-1);
+ $this->wrap_subexpressions = ($parts['type'] === 'operator');
+ $this->convertExpressionParts($expression, [$parts], $extra_expressions);
+ $extra_expressions = array_filter($extra_expressions);
+
+ if ($extra_expressions) {
+ $extra_expressions = array_keys(array_flip($extra_expressions));
+ $item['params'] = '('.$expression.')';
+ $extra_expressions = array_reverse($extra_expressions);
+ $item['params'] .= ' or '.implode(' or ', $extra_expressions);
+ }
+ else {
+ $item['params'] = $expression;
+ }
+
+ return $item;
+ }
+
+ /**
+ * Convert expression part.
+ *
+ * @param string $expression Expression string.
+ * @param array $expression_element Expression part to convert.
+ * @param array $extra_expr Unused parameter to match parent class function signature.
+ */
+ protected function convertSingleExpressionPart(string &$expression, array $expression_element, array &$extra_expr) {
+ if (($this->parser->parse($expression_element['expression'])) === false) {
+ return;
+ }
+
+ $functions = $this->parser->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION);
+
+ for ($i = count($functions) - 1; $i >= 0; $i--) {
+ $fn = $functions[$i]['data'] + ['host' => '', 'item' => '', 'function' => $functions[$i]['value']];
+ $key_param = $fn['functionParams'][0];
+ $host_delimiter_pos = strpos($key_param, ':');
+ $key_param_pos = strpos($key_param, '[');
+ $host_name = '';
+
+ if ($host_delimiter_pos !== false && ($key_param_pos === false || $host_delimiter_pos < $key_param_pos)) {
+ list($host_name, $key_param) = explode(':', $key_param, 2);
+ }
+
+ if ($this->item_key_parser->parse($key_param) === CParser::PARSE_SUCCESS) {
+ array_shift($fn['functionParams']);
+ array_shift($fn['functionParamsRaw']['parameters']);
+ [$new_expression] = $this->convertFunction($fn, $host_name, $key_param);
+
+ $expression_element['expression'] = substr_replace($expression_element['expression'], $new_expression,
+ $functions[$i]['pos'], $functions[$i]['length']
+ );
+ }
+ }
+
+ if ($this->wrap_subexpressions && count($functions) > 1) {
+ $expression_element['expression'] = '('.$expression_element['expression'].')';
+ }
+
+ $expression = substr_replace($expression, $expression_element['expression'],
+ $expression_element['pos'], $expression_element['length']
+ );
+ }
+
+ /**
+ * Check if each particular host reference would be linked through at least one functions according the new trigger
+ * expression syntax.
+ *
+ * @param array $tokens
+ *
+ * @return array
+ */
+ protected function checkHangedFunctionsPerHost(array $tokens): array {
+ $hanged_refs = ['' => false];
+
+ foreach ($tokens as $token) {
+ $host_name = '';
+ $fn = $token['data'];
+ $item_key = $fn['functionParams'][0];
+ $host_delimiter_pos = strpos($item_key, ':');
+
+ if ($host_delimiter_pos === false || $host_delimiter_pos > strpos($item_key, '[')) {
+ continue;
+ }
+
+ $host_name = substr($item_key, 0, $host_delimiter_pos);
+
+ if (!array_key_exists($host_name, $hanged_refs)) {
+ $hanged_refs[$host_name] = false;
+ }
+ if (!in_array($fn['functionName'], $this->standalone_functions)) {
+ $hanged_refs[$host_name] = true;
+ }
+ }
+
+ return $hanged_refs;
+ }
+}
diff --git a/ui/include/classes/import/converters/C52EventNameConverter.php b/ui/include/classes/import/converters/C52EventNameConverter.php
new file mode 100644
index 00000000000..b9e140fb0d8
--- /dev/null
+++ b/ui/include/classes/import/converters/C52EventNameConverter.php
@@ -0,0 +1,95 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Expression macros converter in event name field.
+ */
+class C52EventNameConverter extends C52TriggerExpressionConverter {
+
+ public function __construct() {
+ parent::__construct();
+
+ $this->parser = new C10TriggerExpression(['host_macro' => ['{HOST.HOST}']]);
+ }
+
+ /**
+ * Convert event name.
+ *
+ * @param string $event_name Event name to convert.
+ *
+ * @return string
+ */
+ public function convert($event_name) {
+ $expression_macros = $this->getExpressionMacros($event_name);
+
+ krsort($expression_macros, SORT_NUMERIC);
+
+ foreach ($expression_macros as $pos => $expression_macro) {
+ if (($this->parser->parse(substr($expression_macro, 2, -1), 0)) !== CParser::PARSE_FAIL) {
+ $functions = $this->parser->result->getTokensByType(
+ C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO
+ );
+
+ foreach (array_reverse($functions) as $function) {
+ if ($function['data']['host'] === '{HOST.HOST}' || $function['data']['host'] === '{HOST.HOST1}') {
+ $function['data']['host'] = '';
+ }
+ [$new_expr] = $this->convertFunction($function['data'], '', '');
+ $start = $pos + $function['pos'] + 2;
+ $event_name = substr_replace($event_name, $new_expr, $start, strlen($function['value']));
+ }
+ }
+ }
+
+ return $event_name;
+ }
+
+ /**
+ * Extract expression macros with position in given string.
+ *
+ * @param string $value The string to search in.
+ *
+ * @return array
+ */
+ private function getExpressionMacros(string $value): array {
+ $expr_macro = new C10ExpressionMacroParser();
+ $expression_macros = [];
+ $p = 0;
+
+ while (isset($value[$p])) {
+ if (substr($value, $p, 2) !== '{?') {
+ $p++;
+
+ continue;
+ }
+
+ if ($expr_macro->parse($value, $p) !== CParser::PARSE_FAIL) {
+ $expression_macros[$p] = $expr_macro->getMatch();
+ $p += $expr_macro->getLength();
+ }
+ else {
+ $p++;
+ }
+ }
+
+ return $expression_macros;
+ }
+}
diff --git a/ui/include/classes/import/converters/C52ImportConverter.php b/ui/include/classes/import/converters/C52ImportConverter.php
index 10e213758ba..d06c9190fae 100644..100755
--- a/ui/include/classes/import/converters/C52ImportConverter.php
+++ b/ui/include/classes/import/converters/C52ImportConverter.php
@@ -33,7 +33,6 @@ class C52ImportConverter extends CConverter {
*/
public function convert(array $data): array {
$data['zabbix_export']['version'] = '5.4';
- $templates_names = [];
if (array_key_exists('value_maps', $data['zabbix_export'])) {
/**
@@ -48,7 +47,6 @@ class C52ImportConverter extends CConverter {
}
if (array_key_exists('templates', $data['zabbix_export'])) {
- $templates_names = array_column($data['zabbix_export']['templates'], 'name');
$data['zabbix_export']['templates'] = self::convertTemplates($data['zabbix_export']['templates']);
}
@@ -56,15 +54,11 @@ class C52ImportConverter extends CConverter {
$data['zabbix_export']['maps'] = self::convertMaps($data['zabbix_export']['maps']);
}
- if (array_key_exists('value_maps', $data['zabbix_export'])) {
- $data['zabbix_export'] = self::convertValueMaps($data['zabbix_export']);
- }
-
- if (array_key_exists('triggers', $data['zabbix_export']) && $templates_names) {
- $data['zabbix_export']['triggers'] = self::convertTriggers($data['zabbix_export']['triggers']);
+ if (array_key_exists('triggers', $data['zabbix_export'])) {
+ $data['zabbix_export']['triggers'] = self::convertTriggers($data['zabbix_export']['triggers'], true);
}
- if (array_key_exists('graphs', $data['zabbix_export']) && $templates_names) {
+ if (array_key_exists('graphs', $data['zabbix_export'])) {
$data['zabbix_export']['graphs'] = self::convertGraphs($data['zabbix_export']['graphs']);
}
@@ -96,7 +90,7 @@ class C52ImportConverter extends CConverter {
$host = array_diff_key($host, $tls_fields);
if (array_key_exists('items', $host)) {
- $host['items'] = self::convertItems($host['items']);
+ $host['items'] = self::convertItems($host['items'], $host['host']);
}
if (array_key_exists('interfaces', $host)) {
@@ -104,7 +98,7 @@ class C52ImportConverter extends CConverter {
}
if (array_key_exists('discovery_rules', $host)) {
- $host['discovery_rules'] = self::convertDiscoveryRules($host['discovery_rules']);
+ $host['discovery_rules'] = self::convertDiscoveryRules($host['discovery_rules'], $host['host']);
}
if (array_key_exists('httptests', $host)) {
@@ -131,60 +125,41 @@ class C52ImportConverter extends CConverter {
$old_name_match = '/Template (APP|App|DB|Module|Net|OS|SAN|Server|Tel|VM) (?<mapped_name>.{3,})/';
foreach ($templates as &$template) {
- $template_name = $template['name'];
+ $short_template_name = $template['template'];
+ if (preg_match($old_name_match, $short_template_name, $match)) {
+ $short_template_name = $match['mapped_name'];
+ }
+
+ $template['uuid'] = generateUuidV4($short_template_name);
- if (preg_match($old_name_match, $template_name, $match)) {
- $template_name = $match['mapped_name'];
+ if (array_key_exists('items', $template)) {
+ $template['items'] = self::convertItems($template['items'], $template['template'],
+ $short_template_name);
}
if (array_key_exists('discovery_rules', $template)) {
$template['discovery_rules'] = self::convertDiscoveryRules($template['discovery_rules'],
- $template_name
+ $template['template'], $short_template_name
);
}
if (array_key_exists('httptests', $template)) {
- $template['httptests'] = self::convertHttpTests($template['httptests'], $template_name);
+ $template['httptests'] = self::convertHttpTests($template['httptests'], $short_template_name);
}
unset($template['applications']);
if (array_key_exists('dashboards', $template)) {
- $template['dashboards'] = self::convertTemplateDashboards($template['dashboards'], $template_name);
- }
-
- if (array_key_exists('items', $template)) {
- $template['items'] = self::convertItems($template['items']);
-
- foreach ($template['items'] as &$item) {
- $item['uuid'] = generateUuidV4($template_name.'/'.$item['key']);
-
- if (!array_key_exists('triggers', $item)) {
- continue;
- }
-
- foreach ($item['triggers'] as &$trigger) {
- $seed = $trigger['name'].'/'.$trigger['expression'];
-
- if (array_key_exists('recovery_expression', $trigger)) {
- $seed .= '/'.$trigger['recovery_expression'];
- }
-
- $trigger['uuid'] = generateUuidV4($seed);
- }
- unset($trigger);
- }
- unset($item);
+ $template['dashboards'] = self::convertTemplateDashboards($template['dashboards'],
+ $short_template_name);
}
if (array_key_exists('valuemaps', $template)) {
foreach ($template['valuemaps'] as &$valuemap) {
- $valuemap['uuid'] = generateUuidV4($template_name.'/'.$valuemap['name']);
+ $valuemap['uuid'] = generateUuidV4($short_template_name.'/'.$valuemap['name']);
}
unset($valuemap);
}
-
- $template['uuid'] = generateUuidV4($template_name);
}
unset($template);
@@ -197,11 +172,11 @@ class C52ImportConverter extends CConverter {
* @static
*
* @param array $dashboards
- * @param string $template_name
+ * @param string $short_template_name
*
* @return array
*/
- private static function convertTemplateDashboards(array $dashboards, string $template_name): array {
+ private static function convertTemplateDashboards(array $dashboards, string $short_template_name): array {
$result = [];
foreach ($dashboards as $dashboard) {
@@ -212,7 +187,7 @@ class C52ImportConverter extends CConverter {
}
$dashboard = [
- 'uuid' => generateUuidV4($template_name.'/'.$dashboard['name']),
+ 'uuid' => generateUuidV4($short_template_name.'/'.$dashboard['name']),
'name' => $dashboard['name'],
'pages' => [$dashboard_page]
];
@@ -259,18 +234,191 @@ class C52ImportConverter extends CConverter {
}
/**
+ * Convert discover rules.
+ *
+ * @param array $discovery_rules
+ * @param string $hostname
+ * @param string|null $short_template_name
+ *
+ * @return array
+ */
+ private static function convertDiscoveryRules(array $discovery_rules, string $hostname,
+ ?string $short_template_name = null): array {
+ $result = [];
+
+ foreach ($discovery_rules as $discovery_rule) {
+ if ($short_template_name !== null) {
+ $discovery_rule['uuid'] = generateUuidV4($short_template_name.'/'.$discovery_rule['key']);
+ }
+
+ if (array_key_exists('host_prototypes', $discovery_rule)) {
+ $discovery_rule['host_prototypes'] = self::convertHostPrototypes($discovery_rule['host_prototypes'],
+ $discovery_rule['key'], $short_template_name);
+ }
+
+ if (array_key_exists('item_prototypes', $discovery_rule)) {
+ $discovery_rule['item_prototypes'] = self::convertItemPrototypes($discovery_rule['item_prototypes'],
+ $hostname, $discovery_rule['key'], $short_template_name
+ );
+ }
+
+ if (array_key_exists('trigger_prototypes', $discovery_rule)) {
+ $discovery_rule['trigger_prototypes'] = self::convertTriggers($discovery_rule['trigger_prototypes'],
+ $short_template_name !== null, null, null, $discovery_rule['key']);
+ }
+
+ if (array_key_exists('graph_prototypes', $discovery_rule) && $short_template_name !== null) {
+ foreach ($discovery_rule['graph_prototypes'] as &$graph_prototype) {
+ $seed = $short_template_name.'/'.$discovery_rule['key'].'/'.$graph_prototype['name'];
+ $graph_prototype['uuid'] = generateUuidV4($seed);
+ }
+
+ unset($graph_prototype);
+ }
+
+ $result[] = $discovery_rule;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert item prototypes.
+ *
+ * @static
+ *
+ * @param array $item_prototypes
+ * @param string $hostname
+ * @param string|null $discovery_rule_key
+ * @param string|null $short_template_name
+ *
+ * @return array
+ */
+ private static function convertItemPrototypes(array $item_prototypes, string $hostname,
+ ?string $discovery_rule_key = null, ?string $short_template_name = null): array {
+ $result = [];
+ $calculated_item_converter = new C52CalculatedItemConverter();
+ $aggregate_item_key_converter = new C52AggregateItemKeyConverter();
+
+ foreach ($item_prototypes as $item_prototype) {
+ if (array_key_exists('trigger_prototypes', $item_prototype)) {
+ $item_prototype['trigger_prototypes'] = self::convertTriggers($item_prototype['trigger_prototypes'],
+ $short_template_name !== null, $hostname, $item_prototype['key'], $discovery_rule_key
+ );
+ }
+
+ if ($discovery_rule_key !== null && $short_template_name !== null) {
+ $seed = $short_template_name . '/' . $discovery_rule_key . '/' . $item_prototype['key'];
+ $item_prototype['uuid'] = generateUuidV4($seed);
+ }
+
+ $applications = array_key_exists('applications', $item_prototype) ? $item_prototype['applications'] : [];
+
+ if (array_key_exists('application_prototypes', $item_prototype)) {
+ $applications = array_merge($applications, $item_prototype['application_prototypes']);
+ }
+
+ if ($applications) {
+ $i = 0;
+ $item_prototype['tags'] = [];
+
+ foreach (self::convertApplicationsToTags($applications) as $tag) {
+ $item_prototype['tags']['tag'.($i > 0 ? $i : '')] = $tag;
+ $i++;
+ }
+
+ unset($item_prototype['applications'], $item_prototype['application_prototypes']);
+ }
+
+ if (array_key_exists('type', $item_prototype)) {
+ if ($item_prototype['type'] === CXmlConstantName::CALCULATED) {
+ $item_prototype = $calculated_item_converter->convert($item_prototype);
+ }
+ else if ($item_prototype['type'] === CXmlConstantName::AGGREGATE) {
+ $item_prototype['type'] = CXmlConstantName::CALCULATED;
+ $item_prototype['params'] = $aggregate_item_key_converter->convert($item_prototype['key']);
+ }
+ }
+
+ $result[] = $item_prototype;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert items.
+ *
+ * @static
+ *
+ * @param array $items
+ * @param string $hostname
+ * @param string|null $short_template_name
+ *
+ * @return array
+ */
+ private static function convertItems(array $items, string $hostname, ?string $short_template_name = null): array {
+ $calculated_item_converter = new C52CalculatedItemConverter();
+ $aggregate_item_key_converter = new C52AggregateItemKeyConverter();
+
+ foreach ($items as &$item) {
+ if (array_key_exists('applications', $item)) {
+ $i = 0;
+ $item['tags'] = [];
+
+ foreach (self::convertApplicationsToTags($item['applications']) as $tag) {
+ $item['tags']['tag'.($i > 0 ? $i : '')] = $tag;
+ $i++;
+ }
+
+ unset($item['applications']);
+ }
+
+ if (array_key_exists('triggers', $item)) {
+ $item['triggers'] = self::convertTriggers($item['triggers'], $short_template_name !== null, $hostname,
+ $item['key']);
+ }
+
+ if ($short_template_name !== null) {
+ $item['uuid'] = generateUuidV4($short_template_name.'/'.$item['key']);
+ }
+
+ if (array_key_exists('type', $item)) {
+ if ($item['type'] === CXmlConstantName::CALCULATED) {
+ $item = $calculated_item_converter->convert($item);
+ }
+ else if ($item['type'] === CXmlConstantName::AGGREGATE) {
+ $item['type'] = CXmlConstantName::CALCULATED;
+ $item['params'] = $aggregate_item_key_converter->convert($item['key']);
+ }
+ }
+ }
+ unset($item);
+
+ return $items;
+ }
+
+ /**
* Convert host prototypes.
*
* @static
*
- * @param array $host_prototypes
+ * @param array $host_prototypes
+ * @param string|null $discovery_rule_key
+ * @param string|null $short_template_name
*
* @return array
*/
- private static function convertHostPrototypes(array $host_prototypes): array {
+ private static function convertHostPrototypes(array $host_prototypes, ?string $discovery_rule_key = null,
+ ?string $short_template_name = null): array {
$result = [];
foreach ($host_prototypes as $host_prototype) {
+ if ($discovery_rule_key !== null && $short_template_name !== null) {
+ $seed = $short_template_name.'/'.$discovery_rule_key.'/'.$host_prototype['host'];
+ $host_prototype['uuid'] = generateUuidV4($seed);
+ }
+
if (array_key_exists('interfaces', $host_prototype)) {
$host_prototype['interfaces'] = self::convertInterfaces($host_prototype['interfaces']);
}
@@ -334,128 +482,6 @@ class C52ImportConverter extends CConverter {
}
/**
- * Convert discover rules.
- *
- * @static
- *
- * @param array $discovery_rules
- * @param string $template_name
- *
- * @return array
- */
- private static function convertDiscoveryRules(array $discovery_rules, string $template_name = ''): array {
- $result = [];
-
- foreach ($discovery_rules as $discovery_rule) {
- if (array_key_exists('host_prototypes', $discovery_rule)) {
- $discovery_rule['host_prototypes'] = self::convertHostPrototypes($discovery_rule['host_prototypes']);
- }
-
- if (array_key_exists('item_prototypes', $discovery_rule)) {
- $discovery_rule['item_prototypes'] = self::convertItems($discovery_rule['item_prototypes']);
- }
-
- if ($template_name !== '') {
- if (array_key_exists('host_prototypes', $discovery_rule)) {
- foreach ($discovery_rule['host_prototypes'] as &$host_prototype) {
- $seed = $template_name.'/'.$discovery_rule['key'].'/'.$host_prototype['name'];
- $host_prototype['uuid'] = generateUuidV4($seed);
- }
- unset($host_prototype);
- }
-
- if (array_key_exists('item_prototypes', $discovery_rule)) {
- foreach ($discovery_rule['item_prototypes'] as &$item_prototype) {
- if (array_key_exists('trigger_prototypes', $item_prototype)) {
- foreach ($item_prototype['trigger_prototypes'] as &$trigger_prototype) {
- $seed = $discovery_rule['key'].'/'.$trigger_prototype['name'].'/'
- .$trigger_prototype['expression'];
-
- if (array_key_exists('recovery_expression', $trigger_prototype)) {
- $seed .= '/'.$trigger_prototype['recovery_expression'];
- }
-
- $trigger_prototype['uuid'] = generateUuidV4($seed);
- }
- unset($trigger_prototype);
- }
-
- $seed = $template_name.'/'.$discovery_rule['key'].'/'.$item_prototype['key'];
- $item_prototype['uuid'] = generateUuidV4($seed);
- }
- unset($item_prototype);
-
- if (array_key_exists('trigger_prototypes', $discovery_rule)) {
- foreach ($discovery_rule['trigger_prototypes'] as &$trigger_prototype) {
- $seed = $discovery_rule['key'].'/'.$trigger_prototype['name'].'/'
- .$trigger_prototype['expression'];
-
- if (array_key_exists('recovery_expression', $trigger_prototype)) {
- $seed .= '/'.$trigger_prototype['recovery_expression'];
- }
-
- $trigger_prototype['uuid'] = generateUuidV4($seed);
- }
- unset($trigger_prototype);
- }
- }
-
- if (array_key_exists('graph_prototypes', $discovery_rule)) {
- foreach ($discovery_rule['graph_prototypes'] as &$graph_prototype) {
- $seed = $template_name.'/'.$discovery_rule['key'].'/'.$graph_prototype['name'];
- $graph_prototype['uuid'] = generateUuidV4($seed);
- }
- unset($graph_prototype);
- }
-
- $discovery_rule['uuid'] = generateUuidV4($template_name.'/'.$discovery_rule['key']);
- }
-
- $result[] = $discovery_rule;
- }
-
- return $result;
- }
-
- /**
- * Convert items.
- *
- * @static
- *
- * @param array $items
- *
- * @return array
- */
- private static function convertItems(array $items): array {
- foreach ($items as &$item) {
- $tags = [];
- $i = 0;
-
- if (array_key_exists('applications', $item)) {
- foreach (self::convertApplicationsToTags($item['applications']) as $tag) {
- $tags['tag'.($i > 0 ? $i : '')] = $tag;
- $i++;
- }
- }
-
- if (array_key_exists('application_prototypes', $item)) {
- foreach (self::convertApplicationsToTags($item['application_prototypes']) as $tag) {
- $tags['tag'.($i > 0 ? $i : '')] = $tag;
- $i++;
- }
- }
- unset($item['applications'], $item['application_prototypes']);
-
- if ($tags) {
- $item['tags'] = $tags;
- }
- }
- unset($item);
-
- return $items;
- }
-
- /**
* Convert applications to item tags.
*
* @static
@@ -487,26 +513,32 @@ class C52ImportConverter extends CConverter {
* @return array
*/
private static function convertMaps(array $maps): array {
- foreach ($maps as $i => $map) {
- if (!array_key_exists('selements', $map)) {
- continue;
- }
+ foreach ($maps as &$map) {
+ foreach ($map['selements'] as &$selement) {
+ $selement['evaltype'] = (string) CONDITION_EVAL_TYPE_AND_OR;
- foreach ($map['selements'] as $s => $selement) {
- $maps[$i]['selements'][$s]['evaltype'] = (string) CONDITION_EVAL_TYPE_AND_OR;
+ if ($selement['application'] !== '') {
+ $selement['tags'] = array_map(function ($tag) {
+ return $tag + ['operator' => (string) TAG_OPERATOR_LIKE];
+ }, self::convertApplicationsToTags([['name' => $selement['application']]]));
+ }
+ unset($selement['application']);
- if (array_key_exists('application', $selement) && $selement['application'] !== '') {
- $maps[$i]['selements'][$s]['tags'] = self::convertApplicationsToTags([[
- 'name' => $selement['application']
- ]]);
+ if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
+ $selement['elements'] = self::convertTriggers($selement['elements'], false);
+ }
+ }
+ unset($selement);
- $maps[$i]['selements'][$s]['tags'] = array_map(function ($tag) {
- return $tag + ['operator' => (string) TAG_OPERATOR_LIKE];
- }, $maps[$i]['selements'][$s]['tags']);
+ foreach ($map['links'] as &$link) {
+ foreach ($link['linktriggers'] as &$linktrigger) {
+ $linktrigger['trigger'] = self::convertTriggers([$linktrigger['trigger']], false)[0];
}
- unset($maps[$i]['selements'][$s]['application']);
+ unset($linktrigger);
}
+ unset($link);
}
+ unset($map);
return $maps;
}
@@ -516,15 +548,15 @@ class C52ImportConverter extends CConverter {
*
* @static
*
- * @param array $httptests
- * @param string $template_name
+ * @param array $httptests
+ * @param string|null $short_template_name
*
* @return array
*/
- private static function convertHttpTests(array $httptests, string $template_name = ''): array {
+ private static function convertHttpTests(array $httptests, ?string $short_template_name = null): array {
foreach ($httptests as &$httptest) {
- if ($template_name !== '') {
- $httptest['uuid'] = generateUuidV4($template_name.'/'.$httptest['name']);
+ if ($short_template_name !== null) {
+ $httptest['uuid'] = generateUuidV4($short_template_name.'/'.$httptest['name']);
}
if (array_key_exists('application', $httptest)) {
@@ -537,51 +569,89 @@ class C52ImportConverter extends CConverter {
return $httptests;
}
- private static function convertTriggers(array $triggers): array {
- $result = [];
- $old_name_match = '/Template (APP|App|DB|Module|Net|OS|SAN|Server|Tel|VM) (?<mapped_name>.{3,})/';
- $expression_data = new CTriggerExpression(['lldmacros' => false]);
- $recovery_expression_data = new CTriggerExpression(['lldmacros' => false]);
+ /**
+ * Convert array of triggers.
+ *
+ * @param array $triggers
+ * @param bool $generate_uuid
+ * @param string|null $host
+ * @param string|null $item
+ * @param string|null $discovery_rule_key
+ *
+ * @return array
+ */
+ private static function convertTriggers(array $triggers, bool $generate_uuid, ?string $host = null,
+ ?string $item = null, ?string $discovery_rule_key = null): array {
+ $expression_converter = new C52TriggerExpressionConverter();
+ $event_name_converter = new C52EventNameConverter();
- foreach ($triggers as $trigger) {
- $seed = [$trigger['name']];
+ $old_name_match = '/Template (APP|App|DB|Module|Net|OS|SAN|Server|Tel|VM) (?<mapped_name>.{3,})/';
+ $expression_parser = new CExpressionParser();
- $expression_data->parse($trigger['expression']);
- $template_names = array_unique($expression_data->getHosts());
- $new_trigger_expression = $trigger['expression'];
+ foreach ($triggers as &$trigger) {
+ $trigger['expression'] = $expression_converter->convert([
+ 'expression' => $trigger['expression'],
+ 'host' => $host,
+ 'item' => $item
+ ]);
- foreach ($template_names as $old_name) {
- $new_name = preg_match($old_name_match, $old_name, $match)
- ? $match['mapped_name']
- : $old_name;
- $new_trigger_expression = triggerExpressionReplaceHost($new_trigger_expression, $old_name, $new_name);
+ if (array_key_exists('event_name', $trigger) && $trigger['event_name'] !== '') {
+ $trigger['event_name'] = $event_name_converter->convert($trigger['event_name']);
}
- $seed[] = $new_trigger_expression;
+ if (array_key_exists('recovery_expression', $trigger) && $trigger['recovery_expression'] !== '') {
+ $trigger['recovery_expression'] = $expression_converter->convert([
+ 'expression' => $trigger['recovery_expression'],
+ 'host' => $host,
+ 'item' => $item
+ ]);
+ }
- if (array_key_exists('recovery_expression', $trigger)) {
- $recovery_expression_data->parse($trigger['recovery_expression']);
- $template_names = array_unique($recovery_expression_data->getHosts());
- $new_trigger_recovery_expression = $trigger['recovery_expression'];
+ if (array_key_exists('dependencies', $trigger)) {
+ $trigger['dependencies'] = self::convertTriggers($trigger['dependencies'], false);
+ }
- foreach ($template_names as $old_name) {
- $new_name = preg_match($old_name_match, $old_name, $match)
- ? $match['mapped_name']
- : $old_name;
- $new_trigger_recovery_expression = triggerExpressionReplaceHost($new_trigger_recovery_expression,
- $old_name, $new_name
- );
+ // Generate UUID
+ if ($generate_uuid) {
+ $new_trigger_expression = $trigger['expression'];
+ $seed = $discovery_rule_key !== null
+ ? [$discovery_rule_key.'/'.$trigger['name']]
+ : [$trigger['name']];
+
+ if ($expression_parser->parse($new_trigger_expression) == CParser::PARSE_SUCCESS) {
+ foreach ($expression_parser->getResult()->getHosts() as $old_name) {
+ $new_name = preg_match($old_name_match, $old_name, $match)
+ ? $match['mapped_name']
+ : $old_name;
+ $new_trigger_expression = triggerExpressionReplaceHost($new_trigger_expression, $old_name, $new_name);
+ }
}
- $seed[] = $new_trigger_recovery_expression;
- }
+ $seed[] = $new_trigger_expression;
- $trigger['uuid'] = generateUuidV4(implode('/', $seed));
+ if (array_key_exists('recovery_expression', $trigger)) {
+ $new_trigger_recovery_expression = $trigger['recovery_expression'];
- $result[] = $trigger;
+ if ($expression_parser->parse($new_trigger_recovery_expression) == CParser::PARSE_SUCCESS) {
+ foreach ($expression_parser->getResult()->getHosts() as $old_name) {
+ $new_name = preg_match($old_name_match, $old_name, $match)
+ ? $match['mapped_name']
+ : $old_name;
+ $new_trigger_recovery_expression = triggerExpressionReplaceHost($new_trigger_recovery_expression,
+ $old_name, $new_name
+ );
+ }
+ }
+
+ $seed[] = $new_trigger_recovery_expression;
+ }
+
+ $trigger['uuid'] = generateUuidV4(implode('/', $seed));
+ }
}
+ unset($trigger);
- return $result;
+ return $triggers;
}
/**
@@ -599,7 +669,6 @@ class C52ImportConverter extends CConverter {
foreach ($graphs as $graph) {
$templates_names = array_column($graph['graph_items'], 'host');
-
$seed = [$graph['name']];
foreach ($templates_names as $template_name) {
diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php
new file mode 100644
index 00000000000..a37f8516056
--- /dev/null
+++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php
@@ -0,0 +1,768 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * A converter to convert trigger expression syntax from 5.2 to 5.4.
+ */
+class C52TriggerExpressionConverter extends CConverter {
+
+ /**
+ * Functions which are not related to item.
+ *
+ * @var array
+ */
+ protected $standalone_functions;
+
+ /**
+ * State of each host reference being present in some non-standalone function.
+ *
+ * @var array
+ */
+ protected $hanged_refs = [];
+
+ /**
+ * Host for simplified functions.
+ *
+ * @var string|null
+ */
+ protected $host;
+
+ /**
+ * Item for simplified functions.
+ *
+ * @var string|null
+ */
+ protected $item;
+
+ /**
+ * Either to add parentheses around subexpression.
+ *
+ * @var bool
+ */
+ protected $wrap_subexpressions;
+
+ /**
+ * Old trigger expression syntax parser.
+ *
+ * @var C10TriggerExpression
+ */
+ protected $parser;
+
+ public function __construct() {
+ $this->parser = new C10TriggerExpression(['allow_func_only' => true]);
+ $this->standalone_functions = getStandaloneFunctions();
+ }
+
+ /**
+ * Converts trigger expression to new syntax.
+ *
+ * @param array $trigger_data
+ * @param string $trigger_data['expression']
+ * @param string $trigger_data['host'] (optional)
+ * @param string $trigger_data['item'] (optional)
+ *
+ * @return string
+ */
+ public function convert($trigger_data) {
+ $this->item = (array_key_exists('item', $trigger_data) && $trigger_data['item']) ? $trigger_data['item'] : '';
+ $this->host = (array_key_exists('host', $trigger_data) && $this->item) ? $trigger_data['host'] : '';
+
+ if (($this->parser->parse($trigger_data['expression'])) !== false) {
+ $functions = $this->parser->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO);
+ $this->hanged_refs = $this->checkHangedFunctionsPerHost($functions);
+ $parts = $this->getExpressionParts(0, $this->parser->result->length - 1);
+ $this->wrap_subexpressions = ($parts['type'] === 'operator');
+ $extra_expressions = [];
+ $this->convertExpressionParts($trigger_data['expression'], [$parts], $extra_expressions);
+
+ $extra_expressions = array_filter($extra_expressions);
+ if ($extra_expressions) {
+ $extra_expressions = array_keys(array_flip($extra_expressions));
+
+ $trigger_data['expression'] = '('.$trigger_data['expression'].')';
+
+ $extra_expressions = array_reverse($extra_expressions);
+ $trigger_data['expression'] .= ' or '.implode(' or ', $extra_expressions);
+ }
+ }
+
+ return $trigger_data['expression'];
+ }
+
+ /**
+ * Convert expressions parts.
+ *
+ * @param string $expression Expression string.
+ * @param array $expression_elements Expression tree.
+ * @param array $extra_expr Array to accumulate strings to add at the end of converted expression.
+ */
+ protected function convertExpressionParts(string &$expression, array $expression_elements, array &$extra_expr
+ ): void {
+ for ($i = count($expression_elements) - 1; $i >= 0; $i--) {
+ $part = $expression_elements[$i];
+
+ if ($part['type'] === 'operator') {
+ $this->convertExpressionParts($expression, $part['elements'], $extra_expr);
+ }
+ elseif ($part['type'] === 'expression') {
+ $this->convertSingleExpressionPart($expression, $part, $extra_expr);
+ }
+ }
+ }
+
+ /**
+ * Convert expression part.
+ *
+ * @param string $expression Expression string.
+ * @param array $expression_element Expression part to convert.
+ * @param array $extra_expr Array to accumulate strings to add at the end of converted expression.
+ */
+ protected function convertSingleExpressionPart(string &$expression, array $expression_element, array &$extra_expr) {
+ $expression_data = new C10TriggerExpression(['allow_func_only' => true]);
+
+ if (($expression_data->parse($expression_element['expression'])) !== false) {
+ $fn_list = $expression_data->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO);
+
+ for ($i = count($fn_list) - 1; $i >= 0; $i--) {
+ $fn = $fn_list[$i]['data'];
+ [$new_expression, $_extra_expr] = $this->convertFunction($fn, $this->host, $this->item);
+
+ $extra_expr[] = $_extra_expr;
+
+ $expression_element['expression'] = substr_replace($expression_element['expression'], $new_expression,
+ $fn_list[$i]['pos'], $fn_list[$i]['length']
+ );
+ }
+
+ if ($this->wrap_subexpressions && count($fn_list) > 1) {
+ $expression_element['expression'] = '('.$expression_element['expression'].')';
+ }
+
+ $expression = substr_replace($expression, $expression_element['expression'],
+ $expression_element['pos'], $expression_element['length']
+ );
+ }
+ }
+
+ /**
+ * Convert function to new syntax.
+ *
+ * @param array $fn Function to convert.
+ * @param string $host_name Host name.
+ * @param string $item_key Item key.
+ *
+ * @return array
+ */
+ protected function convertFunction(array $fn, string $host_name, string $item_key): array {
+ if ($fn['item'] === '' && $fn['host'] === '') {
+ $query = sprintf('/%s/%s', $host_name, $item_key);
+ $has_hanged_functions = $this->hanged_refs[''];
+ }
+ else {
+ $query = sprintf('/%s/%s', $fn['host'], $fn['item']);
+ $has_hanged_functions = array_key_exists($fn['host'], $this->hanged_refs)
+ ? $this->hanged_refs[$fn['host']]
+ : false;
+ }
+
+ $extra_expr = '';
+
+ $parameters = [
+ 'unquotable' => array_filter($fn['functionParamsRaw']['parameters'], function ($param) {
+ return ($param['type'] == C10FunctionParser::PARAM_UNQUOTED && $param['raw'] === '');
+ }),
+ 'indicated' => array_filter($fn['functionParamsRaw']['parameters'], function ($param) {
+ return ($param['type'] == C10FunctionParser::PARAM_QUOTED || $param['raw'] !== '');
+ })
+ ];
+
+ switch ($fn['functionName']) {
+ case 'abschange':
+ $new_expression = sprintf('abs(change(%1$s))', $query);
+ break;
+
+ case 'band':
+ $params = self::convertParameters($fn['functionParams'], $parameters, $fn['functionName']);
+ $timeshift = self::paramsToString([$params[0]]);
+ $mask = self::paramsToString([$params[1]]);
+ $new_expression = sprintf('bitand(last(%1$s%2$s)%3$s)', $query, $timeshift, $mask);
+ break;
+
+ case 'change':
+ $new_expression = sprintf('change(%1$s)', $query);
+ break;
+
+ case 'delta':
+ $params = self::convertParameters($fn['functionParams'], $parameters, $fn['functionName']);
+ $params = self::paramsToString($params);
+ $new_expression = sprintf('(max(%1$s%2$s)-min(%1$s%2$s))', $query, $params);
+ break;
+
+ case 'diff':
+ $new_expression = sprintf('(last(%1$s,#1)<>last(%1$s,#2))', $query);
+ break;
+
+ case 'prev':
+ $new_expression = sprintf('last(%1$s,#2)', $query);
+ break;
+
+ case 'trenddelta':
+ $params = self::convertParameters($fn['functionParams'], $parameters, $fn['functionName']);
+ $params = self::paramsToString($params);
+ $new_expression = sprintf('(trendmax(%1$s%2$s)-trendmin(%1$s%2$s))', $query, $params);
+ break;
+
+ case 'iregexp':
+ case 'regexp':
+ case 'str':
+ $params = self::convertParameters($fn['functionParams'], $parameters, $fn['functionName']);
+ $params = self::paramsToString($params);
+ $new_expression = sprintf('find(%1$s%2$s)', $query, $params);
+ break;
+
+ case 'strlen':
+ $params = self::convertParameters($fn['functionParams'], $parameters, $fn['functionName']);
+ $params = self::paramsToString($params);
+ $new_expression = sprintf('length(last(%1$s%2$s))', $query, $params);
+ break;
+
+ case 'date':
+ case 'dayofmonth':
+ case 'dayofweek':
+ case 'time':
+ case 'now':
+ $new_expression = $fn['functionName'].'()';
+ if (!$has_hanged_functions) {
+ $extra_expr = sprintf('(last(%1$s)<>last(%1$s))', $query);
+ }
+ break;
+
+ case 'logseverity':
+ $new_expression = sprintf('logseverity(%1$s)', $query);
+ break;
+
+ default:
+ $new_expression = sprintf('%s(%s%s)', $fn['functionName'], $query,
+ self::paramsToString(self::convertParameters($fn['functionParams'], $parameters,
+ $fn['functionName']
+ ))
+ );
+ break;
+ }
+
+ return [$new_expression, $extra_expr];
+ }
+
+ /**
+ * Convert function parameters to new syntax.
+ *
+ * @param array $parameters List of parameters according previous syntax.
+ * @param array $param_dets
+ * @param array $param_dets['unquotable'] List of numeric indexes for parameters that don't need to be quoted.
+ * @param array $param_dets['indicated'] List of numeric indexes for parameters that are especially indicated and
+ * must be kept.
+ * @param string $fn_name Function name.
+ *
+ * @return array
+ */
+ private static function convertParameters(array $parameters, array $param_dets, string $fn_name): array {
+ switch ($fn_name) {
+ // (sec|#num,<time_shift>)
+ case 'delta':
+ case 'avg':
+ case 'max':
+ case 'min':
+ case 'sum':
+ // (sec|#num,<time_shift>,percentage)
+ case 'percentile':
+ $parameters += ['', ''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ $parameters[1] = self::convertTimeshift($parameters[1]);
+ $parameters[0] = ((string) $parameters[0] === '0') ? '#1' : $parameters[0];
+ if ($parameters[1] !== '') {
+ $parameters[0] .= ':'.$parameters[1];
+ }
+ unset($parameters[1], $param_dets['unquotable'][1], $param_dets['indicated'][1]);
+ break;
+
+ // (sec|#num,<time_shift>,threshold,<fit>)
+ case 'timeleft':
+ $parameters += ['', '', '', ''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ $parameters[1] = self::convertTimeshift($parameters[1]);
+ $parameters[0] = ((string) $parameters[0] === '0') ? '#1' : $parameters[0];
+ if ($parameters[1] !== '') {
+ $parameters[0] .= ':'.$parameters[1];
+ }
+ unset($parameters[1], $param_dets['unquotable'][1], $param_dets['indicated'][1]);
+
+ if ($parameters[3] === '') {
+ // Don't quote unspecified <fit>.
+ $param_dets['unquotable'][3] = true;
+ }
+ break;
+
+ // (<#num>,<time_shift>)
+ case 'strlen':
+ case 'last':
+ $parameters += ['', ''];
+ if (!self::isMacro($parameters[0])
+ && (substr($parameters[0], 0, 1) !== '#'
+ || !ctype_digit(substr($parameters[0], 1))
+ || (int) substr($parameters[0], 1) === 0)) {
+ $parameters[0] = '';
+ }
+
+ $parameters[1] = self::convertTimeshift($parameters[1]);
+ if ($parameters[1] !== '') {
+ $parameters[0] = ($parameters[0] === '') ? '#1' : $parameters[0];
+ $parameters[0] .= ':'.$parameters[1];
+ }
+ unset($parameters[1], $param_dets['unquotable'][1], $param_dets['indicated'][1]);
+ break;
+
+ // (sec|#num,<time_shift>,time,<fit>,<mode>)
+ case 'forecast':
+ $parameters += ['', '', '', '', ''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ $parameters[1] = self::convertTimeshift($parameters[1]);
+ $parameters[0] = ((string) $parameters[0] === '0') ? '#1' : $parameters[0];
+ if ($parameters[1] !== '') {
+ $parameters[0] .= ':'.$parameters[1];
+ }
+ unset($parameters[1], $param_dets['unquotable'][1], $param_dets['indicated'][1]);
+ $parameters[2] = self::convertParamSec($parameters[2]);
+
+ if ($parameters[3] === '') {
+ // Don't quote unspecified <fit>.
+ $param_dets['unquotable'][3] = true;
+ }
+ if ($parameters[4] === '') {
+ // Don't quote unspecified <mode>.
+ $param_dets['unquotable'][4] = true;
+ }
+ break;
+
+ // (<sec|#num>,mask,<time_shift>)
+ case 'band':
+ $parameters += ['', '', ''];
+ if (!self::isMacro($parameters[0])
+ && (substr($parameters[0], 0, 1) !== '#'
+ || !ctype_digit(substr($parameters[0], 1))
+ || (int) substr($parameters[0], 1) === 0)) {
+ $parameters[0] = '';
+ }
+
+ $parameters[2] = self::convertTimeshift($parameters[2]);
+ if ($parameters[2] !== '') {
+ $parameters[0] = ($parameters[0] === '') ? '#1' : $parameters[0];
+ $parameters[0] .= ':'.$parameters[2];
+ }
+ unset($parameters[2], $param_dets['unquotable'][2], $param_dets['indicated'][2]);
+ break;
+
+ // (sec|#num,<pattern>,<operator>,<time_shift>)
+ case 'count':
+ $parameters += ['', '', '', ''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ $parameters[3] = self::convertTimeshift($parameters[3]);
+ $parameters[0] = ((string) $parameters[0] === '0') ? '#1' : $parameters[0];
+ if ($parameters[3] !== '') {
+ $parameters[0] .= ':'.$parameters[3];
+ }
+ if ($parameters[2] === 'band') {
+ $parameters[2] = 'bitand';
+ }
+ elseif ($parameters[2] === '') {
+ // Don't quote unspecified <operator>.
+ $param_dets['unquotable'][2] = true;
+ }
+
+ $parameters[3] = $parameters[1];
+ unset($param_dets['unquotable'][3], $param_dets['indicated'][3], $parameters[1]);
+ if (array_key_exists(1, $param_dets['unquotable'])) {
+ $param_dets['unquotable'][3] = true;
+ unset($param_dets['unquotable'][1]);
+ }
+ if (array_key_exists(1, $param_dets['indicated'])) {
+ $param_dets['indicated'][3] = true;
+ unset($param_dets['indicated'][1]);
+ }
+ break;
+
+ // (sec,<mode>)
+ case 'nodata':
+ $parameters += ['', ''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ if ($parameters[1] === '') {
+ // Don't quote unspecified <mode>.
+ $param_dets['unquotable'][1] = true;
+ }
+ break;
+
+ // (sec)
+ case 'fuzzytime':
+ $parameters += [''];
+ $parameters[0] = self::convertParamSec($parameters[0]);
+ break;
+
+ // (<pattern>,<sec|#num>)
+ case 'iregexp':
+ case 'regexp':
+ case 'str':
+ $parameters += ['', ''];
+ $parameters = [
+ self::convertParamSec($parameters[1]),
+ ($fn_name === 'str') ? 'like' : $fn_name,
+ $parameters[0]
+ ];
+ unset($param_dets['unquotable'][1]);
+ if (array_key_exists(0, $param_dets['indicated'])) {
+ $param_dets['indicated'][2] = true;
+ unset($param_dets['indicated'][0]);
+ }
+
+ break;
+
+ // (period,period_shift)
+ case 'trendavg':
+ case 'trendcount':
+ case 'trenddelta':
+ case 'trendmax':
+ case 'trendmin':
+ case 'trendsum':
+ $parameters += ['', ''];
+ $parameters[0] = self::convertParamPeriod($parameters[0]);
+ if ($parameters[1] !== '') {
+ $parameters[0] .= ':'.$parameters[1];
+ }
+ unset($parameters[1], $param_dets['unquotable'][1], $param_dets['indicated'][1]);
+ break;
+
+ case 'logeventid':
+ case 'logsource':
+ array_unshift($parameters, '');
+ if (array_key_exists(0, $param_dets['indicated'])) {
+ $param_dets['indicated'][1] = true;
+ unset($param_dets['indicated'][0]);
+ }
+ break;
+ }
+
+ // Keys in $parameters array to skip from quoting.
+ $param_dets['unquotable'] = array_keys($param_dets['unquotable']);
+ $param_dets['indicated'] = array_keys($param_dets['indicated']);
+ $functions_with_period_parameter = ['delta', 'avg', 'max', 'min', 'sum', 'last', 'strlen', 'percentile',
+ 'timeleft', 'forecast', 'band', 'count', 'fuzzytime', 'nodata', 'iregexp', 'regexp', 'str', 'trendavg',
+ 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum', 'logeventid', 'logsource'
+ ];
+ if (in_array($fn_name, $functions_with_period_parameter)) {
+ $param_dets['unquotable'][] = 0;
+ }
+
+ if (in_array($fn_name, ['forecast', 'timeleft', 'percentile'])) {
+ // Time parameter don't need to be quoted for forecast() function.
+ $param_dets['unquotable'][] = 2;
+ }
+ elseif ($fn_name === 'band') {
+ // Mask parameter don't need to be quoted for bitand() function.
+ $param_dets['unquotable'][] = 1;
+ }
+
+ array_walk($parameters, function (&$param, $i) use ($param_dets) {
+ if (in_array($i, $param_dets['unquotable'])) {
+ return;
+ }
+
+ if ($param !== '' && $param[0] === '"' && substr($param, -1) === '"') {
+ return;
+ }
+
+ $param = '"'.str_replace('"', '\\"', $param).'"';
+ });
+
+ // Remove empty parameters from the end of the parameters array.
+ foreach (array_reverse(array_keys($parameters)) as $i) {
+ if (in_array($i, $param_dets['indicated'])) {
+ break;
+ }
+
+ if ($parameters[$i] !== '""' && $parameters[$i] !== '') {
+ break;
+ }
+
+ unset($parameters[$i]);
+ }
+
+ return array_values($parameters);
+ }
+
+ /**
+ * Convert seconds.
+ *
+ * @param string $param Parameter to convert.
+ *
+ * @return string
+ */
+ private static function convertParamSec(string $param): string {
+ return (preg_match('/^(?<num>\d+)(?<suffix>['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0)
+ ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's')
+ : $param;
+ }
+
+ /**
+ * Convert period.
+ *
+ * @param string $param Parameter to convert.
+ *
+ * @return string
+ */
+ private static function convertParamPeriod(string $param): string {
+ return (preg_match('/^(?<num>\d+)(?<suffix>[hdwMy]{0,1})$/', $param, $m) && $m['num'] > 0)
+ ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's')
+ : $param;
+ }
+
+ /**
+ * Convert time shift.
+ *
+ * @param string $param Parameter to convert.
+ *
+ * @return string
+ */
+ private static function convertTimeshift(string $param): string {
+ $param = (preg_match('/^(?<num>\d+)(?<suffix>['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0)
+ ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's')
+ : $param;
+
+ return ($param !== '') ? 'now-'.$param : '';
+ }
+
+ /**
+ * Concatenate parameters into comma separated string.
+ *
+ * @param array $parameters Parameter to concatenate.
+ *
+ * @return string
+ */
+ private static function paramsToString(array $parameters): string {
+ $parameters = rtrim(implode(',', $parameters), ',');
+
+ return ($parameters === '') ? '' : ','.$parameters;
+ }
+
+ /**
+ * Check if each particular host reference would be linked through at least one functions according the new trigger
+ * expression syntax.
+ *
+ * @param array $tokens
+ *
+ * @return array
+ */
+ protected function checkHangedFunctionsPerHost(array $tokens): array {
+ $hanged_refs = [];
+
+ foreach ($tokens as $token) {
+ $fn = $token['data'];
+
+ if (!array_key_exists($fn['host'], $hanged_refs)) {
+ $hanged_refs[$fn['host']] = false;
+ }
+ if (!in_array($fn['functionName'], $this->standalone_functions)) {
+ $hanged_refs[$fn['host']] = true;
+ }
+ }
+
+ return $hanged_refs;
+ }
+
+ /**
+ * Split expression into sub-expressions.
+ *
+ * @param int $start
+ * @param int $end
+ *
+ * @return array
+ */
+ protected function getExpressionParts(int $start, int $end): array {
+ $blank_symbols = [' ', "\r", "\n", "\t"];
+
+ $result = [];
+ foreach (['or', 'and'] as $operator) {
+ $operator_found = false;
+ $left_parentheses = -1;
+ $right_parentheses = -1;
+ $expressions = [];
+ $open_symbol_pos = $start;
+ $operator_pos = 0;
+ $operator_token = '';
+
+ for ($i = $start, $level = 0; $i <= $end; $i++) {
+ switch ($this->parser->expression[$i]) {
+ case ' ':
+ case "\r":
+ case "\n":
+ case "\t":
+ if ($open_symbol_pos == $i) {
+ $open_symbol_pos++;
+ }
+ break;
+
+ case '(':
+ if ($level == 0) {
+ $left_parentheses = $i;
+ }
+ $level++;
+ break;
+
+ case ')':
+ $level--;
+ if ($level == 0) {
+ $right_parentheses = $i;
+ }
+ break;
+
+ case '{':
+ case '"':
+ // Skip any previously found tokens starting with brace or double quote.
+ foreach ($this->parser->result->getTokens() as $expression_token) {
+ $expr_endpos = $expression_token['pos'] + $expression_token['length'];
+
+ if ($expression_token['pos'] >= $i && $i < $expr_endpos) {
+ $i = $expr_endpos;
+ break;
+ }
+ }
+ break;
+
+ default:
+ // Try to parse an operator.
+ if ($operator[$operator_pos] === $this->parser->expression[$i]) {
+ $operator_pos++;
+ $operator_token .= $this->parser->expression[$i];
+
+ // Operator found.
+ if ($operator_token === $operator) {
+ /*
+ * Once reached the end of a complete expression, parse the expression on the left side
+ * of the operator.
+ */
+ if ($level == 0) {
+ // Find the last symbol of the expression before the operator.
+ $close_symbol_pos = $i - strlen($operator);
+
+ // Trim blank symbols after the expression.
+ while (in_array($this->parser->expression[$close_symbol_pos], $blank_symbols)) {
+ $close_symbol_pos--;
+ }
+
+ $expressions[] = $this->getExpressionParts($open_symbol_pos, $close_symbol_pos);
+ $open_symbol_pos = $i + 1;
+ $operator_found = true;
+ }
+ $operator_pos = 0;
+ $operator_token = '';
+ }
+ }
+ }
+ }
+
+ // Trim blank symbols in the end of the trigger expression.
+ $close_symbol_pos = $end;
+ while (in_array($this->parser->expression[$close_symbol_pos], $blank_symbols)) {
+ $close_symbol_pos--;
+ }
+
+ /*
+ * We've found a whole expression and parsed the expression on the left side of the operator, parse the
+ * expression on the right.
+ */
+ if ($operator_found) {
+ $expressions[] = $this->getExpressionParts($open_symbol_pos, $close_symbol_pos);
+
+ // Trim blank symbols in the beginning of the trigger expression.
+ $open_symbol_pos = $start;
+ while (in_array($this->parser->expression[$open_symbol_pos], $blank_symbols)) {
+ $open_symbol_pos++;
+ }
+
+ // Trim blank symbols in the end of the trigger expression.
+ $close_symbol_pos = $end;
+ while (in_array($this->parser->expression[$close_symbol_pos], $blank_symbols)) {
+ $close_symbol_pos--;
+ }
+
+ $expr = substr($this->parser->expression, $open_symbol_pos, $close_symbol_pos - $open_symbol_pos + 1);
+ $result = [
+ 'pos' => $open_symbol_pos,
+ 'length' => strlen($expr),
+ 'expression' => $expr,
+ 'type' => 'operator',
+ 'elements' => $expressions
+ ];
+ break;
+ }
+ /*
+ * If we've tried both operators and didn't find anything, it means there's only one expression return the
+ * result.
+ */
+ elseif ($operator === 'and') {
+ // Trim extra parentheses.
+ if ($open_symbol_pos == $left_parentheses && $close_symbol_pos == $right_parentheses) {
+ $open_symbol_pos++;
+ $close_symbol_pos--;
+
+ $result = $this->getExpressionParts($open_symbol_pos, $close_symbol_pos);
+ }
+ // No extra parentheses remain, return the result.
+ else {
+ $expr = substr($this->parser->expression, $open_symbol_pos, $close_symbol_pos - $open_symbol_pos + 1
+ );
+ $result = [
+ 'pos' => $open_symbol_pos,
+ 'length' => strlen($expr),
+ 'expression' => $expr,
+ 'type' => 'expression'
+ ];
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Check if given string is valid user or lld macro.
+ *
+ * @param string $param
+ *
+ * @return bool
+ */
+ private static function isMacro(string $param): bool {
+ foreach ([new CUserMacroParser(), new CLLDMacroParser(), new CLLDMacroFunctionParser()] as $parser) {
+ if ($parser->parse($param) == CParser::PARSE_SUCCESS) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/ui/include/classes/import/importers/CTemplateImporter.php b/ui/include/classes/import/importers/CTemplateImporter.php
index c2ae19ebbdb..50cd820e301 100644
--- a/ui/include/classes/import/importers/CTemplateImporter.php
+++ b/ui/include/classes/import/importers/CTemplateImporter.php
@@ -321,11 +321,13 @@ class CTemplateImporter extends CImporter {
// We need to find template that current element reference to and if it has linked templates
// check all them recursively.
- foreach ($templates[$linked_template_name]['templates'] as $tpl) {
- $circular_templates = $this->checkCircularRecursive($tpl, $templates, $checked);
+ if (array_key_exists($linked_template_name, $templates)) {
+ foreach ($templates[$linked_template_name]['templates'] as $template) {
+ $circular_templates = $this->checkCircularRecursive($template, $templates, $checked);
- if ($circular_templates) {
- return $circular_templates;
+ if ($circular_templates) {
+ return $circular_templates;
+ }
}
}
diff --git a/ui/include/classes/import/validators/C54XmlValidator.php b/ui/include/classes/import/validators/C54XmlValidator.php
index beacdc6aea2..aebcdaac730 100644
--- a/ui/include/classes/import/validators/C54XmlValidator.php
+++ b/ui/include/classes/import/validators/C54XmlValidator.php
@@ -221,7 +221,6 @@ class C54XmlValidator extends CXmlValidatorGeneral {
CXmlConstantValue::ITEM_TYPE_SIMPLE => CXmlConstantName::SIMPLE,
CXmlConstantValue::ITEM_TYPE_INTERNAL => CXmlConstantName::INTERNAL,
CXmlConstantValue::ITEM_TYPE_ZABBIX_ACTIVE => CXmlConstantName::ZABBIX_ACTIVE,
- CXmlConstantValue::ITEM_TYPE_AGGREGATE => CXmlConstantName::AGGREGATE,
CXmlConstantValue::ITEM_TYPE_EXTERNAL => CXmlConstantName::EXTERNAL,
CXmlConstantValue::ITEM_TYPE_ODBC => CXmlConstantName::ODBC,
CXmlConstantValue::ITEM_TYPE_IPMI => CXmlConstantName::IPMI,
diff --git a/ui/include/classes/items/CHelpItems.php b/ui/include/classes/items/CHelpItems.php
index fe20da8eff3..95fd7496bd2 100644
--- a/ui/include/classes/items/CHelpItems.php
+++ b/ui/include/classes/items/CHelpItems.php
@@ -739,24 +739,6 @@ class CHelpItems {
'description' => _('Number of items in the queue which are delayed in Zabbix server or proxy by "from" till "to" seconds, inclusive.')
]
],
- ITEM_TYPE_AGGREGATE => [
- [
- 'key' => 'grpavg[group,key,func,<param>]',
- 'description' => _('Calculates the average value, based on the various parameters supplied. Zabbix server collects aggregate information by doing direct database queries.')
- ],
- [
- 'key' => 'grpmin[group,key,func,<param>]',
- 'description' => _('Calculates the minimum value, based on the various parameters supplied. Zabbix server collects aggregate information by doing direct database queries.')
- ],
- [
- 'key' => 'grpmax[group,key,func,<param>]',
- 'description' => _('Calculates the maximum value, based on the various parameters supplied. Zabbix server collects aggregate information by doing direct database queries.')
- ],
- [
- 'key' => 'grpsum[group,key,func,<param>]',
- 'description' => _('Calculates the sum of values, based on the various parameters supplied. Zabbix server collects aggregate information by doing direct database queries.')
- ]
- ],
ITEM_TYPE_SIMPLE => [
[
'key' => 'icmpping[<target>,<packets>,<interval>,<size>,<timeout>]',
diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php
index dad10dfc82d..c82cc469be3 100644
--- a/ui/include/classes/macros/CMacrosResolver.php
+++ b/ui/include/classes/macros/CMacrosResolver.php
@@ -852,7 +852,7 @@ class CMacrosResolver extends CMacrosResolverGeneral {
}
/**
- * Purpose: Translate {10}>10 to something like {localhost:system.cpu.load.last()}>10
+ * Purpose: Translate {10}>10 to something like last(/localhost/system.cpu.load)>10
*
* @param array $triggers
* @param string $triggers[][<sources>] See options['source']
@@ -863,6 +863,7 @@ class CMacrosResolver extends CMacrosResolverGeneral {
* @param bool $options['resolve_functionids'] Resolve finctionid macros. Default: true.
* @param array $options['sources'] An array of the field names. Default: ['expression'].
* @param string $options['context'] Additional parameter in URL to identify main section.
+ * Default: 'host'.
*
* @return string|array
*/
@@ -872,7 +873,8 @@ class CMacrosResolver extends CMacrosResolverGeneral {
'resolve_usermacros' => false,
'resolve_macros' => false,
'resolve_functionids' => true,
- 'sources' => ['expression']
+ 'sources' => ['expression'],
+ 'context' => 'host'
];
$functionids = [];
@@ -888,26 +890,33 @@ class CMacrosResolver extends CMacrosResolverGeneral {
'usermacros' => true
];
- $expression_data = new CTriggerExpression(['collapsed_expression' => true]);
+ $expression_parser = new CExpressionParser(['collapsed_expression' => true, 'lldmacros' => true]);
// Find macros.
foreach ($triggers as $key => $trigger) {
$functionid_macros = [];
$texts = [];
foreach ($options['sources'] as $source) {
- if ($trigger[$source] !== '' && ($result = $expression_data->parse($trigger[$source])) !== false) {
- foreach ($result->getTokens() as $token) {
+ if ($trigger[$source] !== ''
+ && $expression_parser->parse($trigger[$source]) == CParser::PARSE_SUCCESS) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes([
+ CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_STRING
+ ]);
+
+ foreach ($tokens as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
- $functionid_macros[$token['value']] = null;
+ case CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
+ $functionid_macros[$token['match']] = null;
break;
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- $texts[] = $token['value'];
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ $texts[] = $token['match'];
break;
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
- $texts[] = $token['data']['string'];
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ $texts[] = CExpressionParser::unquoteString($token['match']);
break;
}
}
@@ -1024,11 +1033,6 @@ class CMacrosResolver extends CMacrosResolverGeneral {
if ($options['resolve_macros']) {
$functions = $this->resolveFunctionParameters($functions);
- foreach ($functions as &$function) {
- $function['parameter'] = $function['parameter_expanded'];
- unset($function['parameter_expanded']);
- }
- unset($function);
}
foreach ($macro_values as &$macros) {
@@ -1039,56 +1043,61 @@ class CMacrosResolver extends CMacrosResolverGeneral {
if ($options['html']) {
$style = ($function['status'] == ITEM_STATUS_ACTIVE)
? ($function['state'] == ITEM_STATE_NORMAL) ? ZBX_STYLE_GREEN : ZBX_STYLE_GREY
- : $style = ZBX_STYLE_RED;
+ : ZBX_STYLE_RED;
if ($function['type'] == ITEM_TYPE_HTTPTEST) {
- $link = (new CSpan($function['host'].':'.$function['key_']))->addClass($style);
+ $link = (new CSpan('/'.$function['host'].'/'.$function['key_']))->addClass($style);
}
elseif ($function['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
$link = CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS)
- ? (new CLink($function['host'].':'.$function['key_'],
+ ? (new CLink('/'.$function['host'].'/'.$function['key_'],
(new CUrl('disc_prototypes.php'))
->setArgument('form', 'update')
->setArgument('itemid', $function['itemid'])
->setArgument('parent_discoveryid', $function['parent_itemid'])
- ->setArgument('context', array_key_exists('context', $options)
- ? $options['context']
- : 'host'
- )
+ ->setArgument('context', $options['context'])
))
->addClass(ZBX_STYLE_LINK_ALT)
->addClass($style)
- : (new CSpan($function['host'].':'.$function['key_']))
+ : (new CSpan('/'.$function['host'].'/'.$function['key_']))
->addClass($style);
}
else {
$link = CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS)
- ? (new CLink($function['host'].':'.$function['key_'],
- (new CUrl('items.php'))
- ->setArgument('form', 'update')
- ->setArgument('itemid', $function['itemid'])
- ->setArgument('context', array_key_exists('context', $options)
- ? $options['context']
- : 'host'
- )
- ))
- ->addClass(ZBX_STYLE_LINK_ALT)
- ->setAttribute('data-itemid', $function['itemid'])
- ->addClass($style)
- : (new CSpan($function['host'].':'.$function['key_']))
- ->addClass($style);
+ ? (new CLink('/'.$function['host'].'/'.$function['key_'],
+ (new CUrl('items.php'))
+ ->setArgument('form', 'update')
+ ->setArgument('itemid', $function['itemid'])
+ ->setArgument('context', $options['context'])
+ ))
+ ->addClass(ZBX_STYLE_LINK_ALT)
+ ->setAttribute('data-itemid', $function['itemid'])
+ ->addClass($style)
+ : (new CSpan('/'.$function['host'].'/'.$function['key_']))
+ ->addClass($style);
}
- $value = [
- '{', $link, '.', bold($function['function'].'('), $function['parameter'], bold(')'), '}'
- ];
+ $value = [bold($function['function'].'(')];
+ if (($pos = strpos($function['parameter'], TRIGGER_QUERY_PLACEHOLDER)) !== false) {
+ if ($pos != 0) {
+ $value[] = substr($function['parameter'], 0, $pos);
+ }
+ $value[] = $link;
+ if (strlen($function['parameter']) > $pos + 1) {
+ $value[] = substr($function['parameter'], $pos + 1);
+ }
+ }
+ else {
+ $value[] = $function['parameter'];
+ }
+ $value[] = bold(')');
}
else {
- $value = '{'.
- $function['host'].':'.
- $function['key_'].'.'.
- $function['function'].'('.$function['parameter'].')'.
- '}';
+ $query = '/'.$function['host'].'/'.$function['key_'];
+ $params = (($pos = strpos($function['parameter'], TRIGGER_QUERY_PLACEHOLDER)) !== false)
+ ? substr_replace($function['parameter'], $query, $pos, 1)
+ : $function['parameter'];
+ $value = $function['function'].'('.$params.')';
}
}
else {
@@ -1132,56 +1141,93 @@ class CMacrosResolver extends CMacrosResolverGeneral {
// Replace macros to value.
foreach ($triggers as $key => $trigger) {
foreach ($options['sources'] as $source) {
- if ($trigger[$source] === '' || ($result = $expression_data->parse($trigger[$source])) === false) {
+ if ($trigger[$source] === ''
+ || $expression_parser->parse($trigger[$source]) != CParser::PARSE_SUCCESS) {
continue;
}
$expression = [];
$pos_left = 0;
- foreach ($result->getTokens() as $token) {
+ $token_types = [
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ CExpressionParserResult::TOKEN_TYPE_STRING
+ ];
+ if ($options['resolve_functionids']) {
+ $token_types[] = CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO;
+ }
+ if ($options['html']) {
+ $token_types[] = CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION;
+ }
+
+ $rigth_parentheses = [];
+ $tokens = $expression_parser->getResult()->getTokensOfTypes($token_types);
+
+ foreach ($tokens as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
- if (!$options['resolve_functionids']) {
- continue 2;
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ case CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ foreach ($rigth_parentheses as $pos => $value) {
+ if ($pos < $token['pos']) {
+ if ($pos_left != $pos) {
+ $expression[] = substr($trigger[$source], $pos_left, $pos - $pos_left);
+ }
+ $expression[] = bold($value);
+ $pos_left = $pos + strlen($value);
+ unset($rigth_parentheses[$pos]);
+ }
}
- // break; is not missing here
-
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
if ($pos_left != $token['pos']) {
$expression[] = substr($trigger[$source], $pos_left, $token['pos'] - $pos_left);
}
- $pos_left = $token['pos'] + $token['length'];
+ $pos_left = ($token['type'] == CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION)
+ ? $token['pos'] + strlen($token['data']['function']) + 1
+ : $token['pos'] + $token['length'];
break;
}
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
- $expression[] = $macro_values[$key][$token['value']];
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ $expression[] = bold($token['data']['function'].'(');
+ $rigth_parentheses[$token['pos'] + $token['length'] - 1] = ')';
+ ksort($rigth_parentheses, SORT_NUMERIC);
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO:
+ $expression[] = $macro_values[$key][$token['match']];
break;
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- if (array_key_exists($token['value'], $usermacro_values[$key])) {
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ if (array_key_exists($token['match'], $usermacro_values[$key])) {
$expression[] =
- CTriggerExpression::quoteString($usermacro_values[$key][$token['value']], false);
+ CExpressionParser::quoteString($usermacro_values[$key][$token['match']], false);
}
else {
$expression[] = ($options['resolve_usermacros'] && $options['html'])
? (new CSpan('*ERROR*'))->addClass(ZBX_STYLE_RED)
- : $token['value'];
+ : $token['match'];
}
break;
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
- $string = strtr($token['data']['string'], $usermacro_values[$key]);
- $expression[] = CTriggerExpression::quoteString($string, false, true);
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ $string = strtr(CExpressionParser::unquoteString($token['match']), $usermacro_values[$key]);
+ $expression[] = CExpressionParser::quoteString($string, false, true);
break;
-
}
}
- if ($pos_left != strlen($trigger[$source])) {
+ $len = strlen($trigger[$source]);
+ foreach ($rigth_parentheses as $pos => $value) {
+ if ($pos_left != $pos) {
+ $expression[] = substr($trigger[$source], $pos_left, $pos - $pos_left);
+ }
+ $expression[] = bold($value);
+ $pos_left = $pos + strlen($value);
+ unset($rigth_parentheses[$pos]);
+ }
+ if ($pos_left != $len) {
$expression[] = substr($trigger[$source], $pos_left);
}
@@ -1815,7 +1861,7 @@ class CMacrosResolver extends CMacrosResolverGeneral {
}
/**
- * Resolve function parameter macros to "parameter_expanded" field.
+ * Resolve function parameter macros.
*
* @param array $functions
* @param string $functions[n]['hostid']
@@ -1825,20 +1871,20 @@ class CMacrosResolver extends CMacrosResolverGeneral {
* @return array
*/
public function resolveFunctionParameters(array $functions) {
- foreach ($functions as &$function) {
- $function['parameter_expanded'] = $function['parameter'];
- }
- unset($function);
-
$types = ['usermacros' => true];
$macro_values = [];
$usermacros = [];
foreach ($functions as $key => $function) {
- $matched_macros = $this->extractFunctionMacros($function['function'].'('.$function['parameter'].')',
- $types
- );
+ $functions[$key]['function_string'] = $function['function'].'('.$function['parameter'].')';
+ if (($pos = strpos($functions[$key]['function_string'], TRIGGER_QUERY_PLACEHOLDER)) !== false) {
+ $functions[$key]['function_string'] = substr_replace($functions[$key]['function_string'], '/foo/bar',
+ $pos, 1
+ );
+ $functions[$key]['function_query_pos'] = $pos;
+ }
+ $matched_macros = $this->extractFunctionMacros($functions[$key]['function_string'], $types);
if ($matched_macros['usermacros']) {
$usermacros[$key] = ['hostids' => [$function['hostid']], 'macros' => $matched_macros['usermacros']];
}
@@ -1854,11 +1900,15 @@ class CMacrosResolver extends CMacrosResolverGeneral {
// Replace macros to value.
foreach ($macro_values as $key => $macros) {
- $function = $functions[$key]['function'].'('.$functions[$key]['parameter'].')';
- $function = $this->resolveFunctionMacros($function, $macros, $types);
- $functions[$key]['parameter_expanded'] = substr($function, strlen($functions[$key]['function']) + 1, -1);
+ $function = $this->resolveFunctionMacros($functions[$key]['function_string'], $macros, $types);
+ $function = substr_replace($function, TRIGGER_QUERY_PLACEHOLDER, $functions[$key]['function_query_pos'], 8);
+ $functions[$key]['parameter'] = substr($function, strlen($functions[$key]['function']) + 1, -1);
}
+ array_walk($functions, function (array &$function) {
+ unset($function['function_string'], $function['function_query_pos']);
+ });
+
return $functions;
}
@@ -1907,7 +1957,7 @@ class CMacrosResolver extends CMacrosResolverGeneral {
}
// Try to create valid expression.
- $expressionData = new CTriggerExpression();
+ $expressionData = new C10TriggerExpression();
if (!$expressionData->parse($macro) || !isset($expressionData->expressions[0])) {
continue;
diff --git a/ui/include/classes/macros/CMacrosResolverGeneral.php b/ui/include/classes/macros/CMacrosResolverGeneral.php
index 26fbc6e7fda..f2aefdc3600 100644
--- a/ui/include/classes/macros/CMacrosResolverGeneral.php
+++ b/ui/include/classes/macros/CMacrosResolverGeneral.php
@@ -54,18 +54,18 @@ class CMacrosResolverGeneral {
*/
protected function resolveTriggerReferences($expression, $references) {
$values = [];
- $expression_data = new CTriggerExpression(['collapsed_expression' => true]);
+ $expression_parser = new CExpressionParser(['collapsed_expression' => true, 'lldmacros' => true]);
- if (($result = $expression_data->parse($expression)) !== false) {
- foreach ($result->getTokens() as $token) {
+ if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
+ foreach ($expression_parser->getResult()->getTokens() as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_NUMBER:
- case CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO:
- $values[] = $token['value'];
+ case CExpressionParserResult::TOKEN_TYPE_NUMBER:
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ $values[] = $token['match'];
break;
- case CTriggerExprParserResult::TOKEN_TYPE_STRING:
- $values[] = $token['data']['string'];
+ case CExpressionParserResult::TOKEN_TYPE_STRING:
+ $values[] = CExpressionParser::unquoteString($token['match']);
break;
}
}
@@ -388,13 +388,13 @@ class CMacrosResolverGeneral {
$function_parameters = [];
foreach ($function_parser->getParamsRaw()['parameters'] as $param_raw) {
- switch ($param_raw['type']) {
- case CFunctionParser::PARAM_UNQUOTED:
- $function_parameters[] = $param_raw['raw'];
+ switch ($param_raw->type) {
+ case C10FunctionParser::PARAM_UNQUOTED:
+ $function_parameters[] = $param_raw->match;
break;
- case CFunctionParser::PARAM_QUOTED:
- $function_parameters[] = CFunctionParser::unquoteParam($param_raw['raw']);
+ case C10FunctionParser::PARAM_QUOTED:
+ $function_parameters[] = C10FunctionParser::unquoteParam($param_raw->match);
break;
}
}
@@ -500,24 +500,25 @@ class CMacrosResolverGeneral {
/**
* Extract macros from a trigger function.
*
- * @param string $function a trigger function, for example 'last({$LAST})'
+ * @param string $function a history function, for example 'last(/host/key, {$OFFSET})'
* @param array $types the types of macros (see extractMacros() for more details)
*
* @return array see extractMacros() for more details
*/
protected function extractFunctionMacros($function, array $types) {
- $function_parser = new CFunctionParser();
-
+ $hist_function_parser = new CHistFunctionParser(['usermacros' => true, 'lldmacros' => true]);
$function_parameters = [];
- if ($function_parser->parse($function) == CParser::PARSE_SUCCESS) {
- foreach ($function_parser->getParamsRaw()['parameters'] as $param_raw) {
- switch ($param_raw['type']) {
- case CFunctionParser::PARAM_UNQUOTED:
- $function_parameters[] = $param_raw['raw'];
+
+ if ($hist_function_parser->parse($function) == CParser::PARSE_SUCCESS) {
+ foreach ($hist_function_parser->getParameters() as $parameter) {
+ switch ($parameter['type']) {
+ case CHistFunctionParser::PARAM_TYPE_PERIOD:
+ case CHistFunctionParser::PARAM_TYPE_UNQUOTED:
+ $function_parameters[] = $parameter['match'];
break;
- case CFunctionParser::PARAM_QUOTED:
- $function_parameters[] = CFunctionParser::unquoteParam($param_raw['raw']);
+ case CHistFunctionParser::PARAM_TYPE_QUOTED:
+ $function_parameters[] = CHistFunctionParser::unquoteParam($parameter['match']);
break;
}
}
@@ -597,37 +598,30 @@ class CMacrosResolverGeneral {
* @return string
*/
protected function resolveFunctionMacros($function, array $macros, array $types) {
- $function_parser = new CFunctionParser();
-
- if ($function_parser->parse($function) == CParser::PARSE_SUCCESS) {
- $params_raw = $function_parser->getParamsRaw();
- $function_chain = $params_raw['raw'];
+ $hist_function_parser = new CHistFunctionParser(['usermacros' => true, 'lldmacros' => true]);
- foreach (array_reverse($params_raw['parameters']) as $param_raw) {
- $param = $param_raw['raw'];
+ if ($hist_function_parser->parse($function) == CParser::PARSE_SUCCESS) {
+ foreach (array_reverse($hist_function_parser->getParameters()) as $parameter) {
+ $param = $parameter['match'];
$forced = false;
- switch ($param_raw['type']) {
- case CFunctionParser::PARAM_QUOTED:
- $param = CFunctionParser::unquoteParam($param);
- $forced = true;
- // break; is not missing here
+ if ($parameter['type'] == CHistFunctionParser::PARAM_TYPE_QUOTED) {
+ $param = CHistFunctionParser::unquoteParam($param);
+ $forced = true;
+ }
- case CFunctionParser::PARAM_UNQUOTED:
- $matched_macros = $this->getMacroPositions($param, $types);
+ $matched_macros = $this->getMacroPositions($param, $types);
- foreach (array_reverse($matched_macros, true) as $pos => $macro) {
- $param = substr_replace($param, $macros[$macro], $pos, strlen($macro));
- }
+ foreach (array_reverse($matched_macros, true) as $pos => $macro) {
+ $param = substr_replace($param, $macros[$macro], $pos, strlen($macro));
+ }
- $param = quoteFunctionParam($param, $forced);
- break;
+ if ($parameter['type'] != CHistFunctionParser::PARAM_TYPE_PERIOD) {
+ $param = quoteFunctionParam($param, $forced);
}
- $function_chain = substr_replace($function_chain, $param, $param_raw['pos'], strlen($param_raw['raw']));
+ $function = substr_replace($function, $param, $parameter['pos'], $parameter['length']);
}
-
- $function = substr_replace($function, $function_chain, $params_raw['pos'], strlen($params_raw['raw']));
}
return $function;
diff --git a/ui/include/classes/macros/CMacrosResolverHelper.php b/ui/include/classes/macros/CMacrosResolverHelper.php
index 026e769f808..9b7af0f5da5 100644
--- a/ui/include/classes/macros/CMacrosResolverHelper.php
+++ b/ui/include/classes/macros/CMacrosResolverHelper.php
@@ -577,23 +577,6 @@ class CMacrosResolverHelper {
}
/**
- * Resolve function parameter macros to "parameter_expanded" field.
- *
- * @static
- *
- * @param array $data
- * @param string $data[n]['hostid']
- * @param string $data[n]['parameter']
- *
- * @return array
- */
- public static function resolveFunctionParameters(array $data) {
- self::init();
-
- return self::$macrosResolver->resolveFunctionParameters($data);
- }
-
- /**
* Expand functional macros in given map label.
*
* @param string $label label to expand
diff --git a/ui/include/classes/parsers/C10ExpressionMacroParser.php b/ui/include/classes/parsers/C10ExpressionMacroParser.php
new file mode 100644
index 00000000000..a23b91bc49b
--- /dev/null
+++ b/ui/include/classes/parsers/C10ExpressionMacroParser.php
@@ -0,0 +1,81 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * A parser for function macros like "{?<trigger expression>}".
+ */
+class C10ExpressionMacroParser extends CParser {
+
+ /**
+ * @var C10TriggerExpression
+ */
+ protected $trigger_expression_parser;
+
+ /**
+ * Set up necessary parsers.
+ */
+ public function __construct() {
+ $this->trigger_expression_parser = new C10TriggerExpression([
+ 'host_macro' => ['{HOST.HOST}']
+ ]);
+ }
+
+ /**
+ * @param string $source
+ * @param int $pos
+ *
+ * @return int
+ */
+ public function parse($source, $pos = 0) {
+ $this->length = 0;
+ $this->match = '';
+
+ $p = $pos;
+
+ if (!isset($source[$p]) || substr($source, $p, 2) !== '{?') {
+ $this->errorPos($source, $p);
+
+ return CParser::PARSE_FAIL;
+ }
+ $p += 2;
+
+ $this->trigger_expression_parser->parse(substr($source, $p));
+
+ if ($this->trigger_expression_parser->error_type !== C10TriggerExpression::ERROR_UNPARSED_CONTENT) {
+ $this->errorPos($source, $p + $this->trigger_expression_parser->error_pos);
+
+ return CParser::PARSE_FAIL;
+ }
+ $p += $this->trigger_expression_parser->error_pos;
+
+ if (!isset($source[$p]) || $source[$p] !== '}') {
+ $this->errorPos($source, $p);
+
+ return CParser::PARSE_FAIL;
+ }
+ $p++;
+
+ $this->length = $p - $pos;
+ $this->match = substr($source, $pos, $this->length);
+
+ return (isset($source[$pos + $this->length]) ? CParser::PARSE_SUCCESS_CONT : CParser::PARSE_SUCCESS);
+ }
+}
diff --git a/ui/include/classes/parsers/CFunctionMacroParser.php b/ui/include/classes/parsers/C10FunctionMacroParser.php
index 085b57e2116..65f3cf792f1 100644
--- a/ui/include/classes/parsers/CFunctionMacroParser.php
+++ b/ui/include/classes/parsers/C10FunctionMacroParser.php
@@ -20,9 +20,9 @@
/**
- * A parser for function macros like "{host.item.func()}".
+ * A parser for function macros like "{host:item.func()}".
*/
-class CFunctionMacroParser extends CParser {
+class C10FunctionMacroParser extends CParser {
/**
* An options array.
@@ -48,7 +48,7 @@ class CFunctionMacroParser extends CParser {
/**
* Parser for trigger functions.
*
- * @var CFunctionParser
+ * @var C10FunctionParser
*/
private $function_parser;
@@ -69,7 +69,7 @@ class CFunctionMacroParser extends CParser {
public function __construct($options = []) {
$this->options = $options + $this->options;
$this->item_key_parser = new CItemKey(['18_simple_checks' => $this->options['18_simple_checks']]);
- $this->function_parser = new CFunctionParser();
+ $this->function_parser = new C10FunctionParser();
$this->host_name_parser = new CHostNameParser();
if ($this->options['host_macro']) {
diff --git a/ui/include/classes/parsers/CFunctionParser.php b/ui/include/classes/parsers/C10FunctionParser.php
index 364f50bb402..9a64300677f 100644
--- a/ui/include/classes/parsers/CFunctionParser.php
+++ b/ui/include/classes/parsers/C10FunctionParser.php
@@ -20,9 +20,9 @@
/**
- * Class is used to validate and parse the trigger function.
+ * Class is used to validate and parse a trigger function.
*/
-class CFunctionParser extends CParser {
+class C10FunctionParser extends CParser {
const STATE_NEW = 0;
const STATE_END = 1;
diff --git a/ui/include/classes/parsers/CTriggerExpression.php b/ui/include/classes/parsers/C10TriggerExpression.php
index 30e26350ab0..2ff4e0df9ab 100644
--- a/ui/include/classes/parsers/CTriggerExpression.php
+++ b/ui/include/classes/parsers/C10TriggerExpression.php
@@ -19,7 +19,10 @@
**/
-class CTriggerExpression {
+/**
+ * Trigger expressions parser.
+ */
+class C10TriggerExpression {
// For parsing of trigger expression.
const STATE_AFTER_OPEN_BRACE = 1;
const STATE_AFTER_BINARY_OPERATOR = 2;
@@ -77,7 +80,6 @@ class CTriggerExpression {
*
* Supported options:
* 'lldmacros' => true Enable low-level discovery macros usage in trigger expression.
- * 'lowercase_errors' => false Return error messages in lowercase.
* 'allow_func_only' => true Allow trigger expression without host:key pair, i.e. {func(param)}.
* 'collapsed_expression' => true Short trigger expression.
* For example: {439} > {$MAX_THRESHOLD} or {439} < {$MIN_THRESHOLD}
@@ -88,7 +90,6 @@ class CTriggerExpression {
*/
public $options = [
'lldmacros' => true,
- 'lowercase_errors' => false,
'allow_func_only' => false,
'collapsed_expression' => false,
'calculated' => false,
@@ -105,7 +106,7 @@ class CTriggerExpression {
/**
* Object containing the results of parsing.
*
- * @var CTriggerExprParserResult
+ * @var C10TriggerExprParserResult
*/
public $result;
@@ -147,7 +148,7 @@ class CTriggerExpression {
/**
* Parser for function macros.
*
- * @var CFunctionMacroParser
+ * @var C10FunctionMacroParser
*/
protected $function_macro_parser;
@@ -161,7 +162,7 @@ class CTriggerExpression {
/**
* Parser for trigger functions.
*
- * @var CFunctionParser
+ * @var C10FunctionParser
*/
protected $function_parser;
@@ -203,7 +204,6 @@ class CTriggerExpression {
/**
* @param array $options
* @param bool $options['lldmacros']
- * @param bool $options['lowercase_errors']
* @param bool $options['allow_func_only']
* @param bool $options['collapsed_expression']
* @param bool $options['calculated']
@@ -220,9 +220,9 @@ class CTriggerExpression {
$this->functionid_parser = new CFunctionIdParser();
}
else {
- $this->function_macro_parser = new CFunctionMacroParser(['host_macro' => $this->options['host_macro']]);
+ $this->function_macro_parser = new C10FunctionMacroParser(['host_macro' => $this->options['host_macro']]);
}
- $this->function_parser = new CFunctionParser();
+ $this->function_parser = new C10FunctionParser();
$this->lld_macro_parser = new CLLDMacroParser();
$this->lld_macro_function_parser = new CLLDMacroFunctionParser;
$this->user_macro_parser = new CUserMacroParser();
@@ -254,11 +254,11 @@ class CTriggerExpression {
*
* @param string $expression
*
- * @return CTriggerExprParserResult|bool returns a result object if a match has been found or false otherwise
+ * @return C10TriggerExprParserResult|bool returns a result object if a match has been found or false otherwise
*/
public function parse($expression) {
// initializing local variables
- $this->result = new CTriggerExprParserResult();
+ $this->result = new C10TriggerExprParserResult();
$this->isValid = true;
$this->error = '';
$this->error_type = 0;
@@ -291,14 +291,14 @@ class CTriggerExpression {
switch ($char) {
case '-':
$state = self::STATE_AFTER_MINUS_OPERATOR;
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
$char, $this->pos, 1
);
break;
case '(':
$state = self::STATE_AFTER_OPEN_BRACE;
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
$char, $this->pos, 1
);
$level++;
@@ -306,7 +306,7 @@ class CTriggerExpression {
default:
if ($this->parseUsing($this->notOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_NOT_OPERATOR;
}
elseif ($this->parseConstant()) {
@@ -322,14 +322,14 @@ class CTriggerExpression {
switch ($char) {
case '-':
$state = self::STATE_AFTER_MINUS_OPERATOR;
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
$char, $this->pos, 1
);
break;
case '(':
$state = self::STATE_AFTER_OPEN_BRACE;
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
$char, $this->pos, 1
);
$level++;
@@ -346,7 +346,7 @@ class CTriggerExpression {
}
if ($this->parseUsing($this->notOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_NOT_OPERATOR;
}
else {
@@ -361,14 +361,14 @@ class CTriggerExpression {
if (!$afterSpace) {
break 3;
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
$char, $this->pos, 1
);
$state = self::STATE_AFTER_MINUS_OPERATOR;
break;
case '(':
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
$char, $this->pos, 1
);
$state = self::STATE_AFTER_OPEN_BRACE;
@@ -381,7 +381,7 @@ class CTriggerExpression {
}
if ($this->parseUsing($this->notOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_NOT_OPERATOR;
}
elseif ($this->parseConstant()) {
@@ -399,7 +399,7 @@ class CTriggerExpression {
if ($level == 0) {
break 3;
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
$char, $this->pos, 1
);
$level--;
@@ -407,13 +407,13 @@ class CTriggerExpression {
default:
if ($this->parseUsing($this->binaryOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_BINARY_OPERATOR;
break;
}
if ($this->parseUsing($this->logicalOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_LOGICAL_OPERATOR;
break;
}
@@ -429,7 +429,7 @@ class CTriggerExpression {
if ($level == 0) {
break 3;
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
$char, $this->pos, 1
);
$level--;
@@ -438,7 +438,7 @@ class CTriggerExpression {
default:
if ($this->parseUsing($this->binaryOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_BINARY_OPERATOR;
break;
}
@@ -448,7 +448,7 @@ class CTriggerExpression {
}
if ($this->parseUsing($this->logicalOperatorParser,
- CTriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR)) {
$state = self::STATE_AFTER_LOGICAL_OPERATOR;
}
else {
@@ -463,14 +463,14 @@ class CTriggerExpression {
if (!$afterSpace) {
break 3;
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
$char, $this->pos, 1
);
$state = self::STATE_AFTER_MINUS_OPERATOR;
break;
case '(':
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
$char, $this->pos, 1
);
$state = self::STATE_AFTER_OPEN_BRACE;
@@ -494,7 +494,7 @@ class CTriggerExpression {
case self::STATE_AFTER_MINUS_OPERATOR:
switch ($char) {
case '(':
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
$char, $this->pos, 1
);
$state = self::STATE_AFTER_OPEN_BRACE;
@@ -517,14 +517,9 @@ class CTriggerExpression {
}
if ($this->pos == 0) {
- if ($this->options['calculated']) {
- $this->error = _('incorrect calculated item formula');
- }
- else {
- $this->error = $this->options['lowercase_errors']
- ? _('incorrect trigger expression')
- : _('Incorrect trigger expression.');
- }
+ $this->error = $this->options['calculated']
+ ? _('incorrect calculated item formula')
+ : _('Incorrect trigger expression.');
$this->isValid = false;
}
@@ -538,15 +533,9 @@ class CTriggerExpression {
if ($error) {
$exp_part = substr($this->expression, ($this->pos == 0) ? 0 : $this->pos - 1);
- if ($this->options['calculated']) {
- $this->error = _s('incorrect calculated item formula starting from "%1$s"', $exp_part);
- }
- else {
- $this->error = $this->options['lowercase_errors']
- ? _s('incorrect trigger expression starting from "%1$s"', $exp_part)
- : _('Incorrect trigger expression.').' '.
- _s('Check expression part starting from "%1$s".', $exp_part);
- }
+ $this->error = $this->options['calculated']
+ ? _s('incorrect calculated item formula starting from "%1$s"', $exp_part)
+ : _('Incorrect trigger expression.').' '._s('Check expression part starting from "%1$s".', $exp_part);
$this->error_type = $error;
$this->error_pos = $this->pos;
$this->isValid = false;
@@ -611,7 +600,7 @@ class CTriggerExpression {
*/
private function parseConstant() {
if ($this->parseNumber() || $this->parseString()
- || $this->parseUsing($this->user_macro_parser, CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO)) {
+ || $this->parseUsing($this->user_macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_USER_MACRO)) {
return true;
}
@@ -621,15 +610,15 @@ class CTriggerExpression {
}
}
elseif ($this->parseFunctionMacro()
- || $this->parseUsing($this->macro_parser, CTriggerExprParserResult::TOKEN_TYPE_MACRO)) {
+ || $this->parseUsing($this->macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_MACRO)) {
return true;
}
// LLD macro support for trigger prototypes.
if ($this->options['lldmacros']) {
- if ($this->parseUsing($this->lld_macro_parser, CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)
+ if ($this->parseUsing($this->lld_macro_parser, C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)
|| $this->parseUsing($this->lld_macro_function_parser,
- CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)) {
+ C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)) {
return true;
}
}
@@ -670,13 +659,14 @@ class CTriggerExpression {
$expression = substr($this->expression, $this->pos, $pos + 1 - $this->pos);
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
$expression, $this->pos, $this->function_parser->getLength() + 2,
[
'host' => '',
'item' => '',
'function' => $this->function_parser->getMatch(),
'functionName' => $this->function_parser->getFunction(),
+ 'functionParamsRaw' => $this->function_parser->getParamsRaw(),
'functionParams' => $function_param_list
]
);
@@ -705,7 +695,7 @@ class CTriggerExpression {
*/
private function parseFunctionMacro() {
if ($this->options['collapsed_expression']) {
- return $this->parseUsing($this->functionid_parser, CTriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO);
+ return $this->parseUsing($this->functionid_parser, C10TriggerExprParserResult::TOKEN_TYPE_FUNCTIONID_MACRO);
}
else {
return $this->parseSimpleMacro();
@@ -737,13 +727,14 @@ class CTriggerExpression {
$function_param_list[] = $this->function_parser->getParam($n);
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
$this->function_macro_parser->getMatch(), $startPos, $this->function_macro_parser->getLength(),
[
'host' => $this->function_macro_parser->getHost(),
'item' => $this->function_macro_parser->getItem(),
'function' => $this->function_macro_parser->getFunction(),
'functionName' => $this->function_parser->getFunction(),
+ 'functionParamsRaw' => $this->function_parser->getParamsRaw(),
'functionParams' => $function_param_list
]
);
@@ -783,10 +774,11 @@ class CTriggerExpression {
$function_param_list[] = $this->function_parser->getParam($n);
}
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_FUNCTION,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION,
$this->function_parser->getMatch(), $startPos, $this->function_parser->getLength(),
[
'functionName' => $this->function_parser->getFunction(),
+ 'functionParamsRaw' => $this->function_parser->getParamsRaw(),
'functionParams' => $function_param_list
]
);
@@ -823,7 +815,7 @@ class CTriggerExpression {
];
$this->result->addToken(
- CTriggerExprParserResult::TOKEN_TYPE_NUMBER,
+ C10TriggerExprParserResult::TOKEN_TYPE_NUMBER,
$this->number_parser->getMatch(),
$this->pos,
$this->number_parser->getLength(),
@@ -848,7 +840,7 @@ class CTriggerExpression {
$len = strlen($matches[0]);
- $this->result->addToken(CTriggerExprParserResult::TOKEN_TYPE_STRING, $matches[0], $this->pos, $len,
+ $this->result->addToken(C10TriggerExprParserResult::TOKEN_TYPE_STRING, $matches[0], $this->pos, $len,
['string' => self::unquoteString($matches[0])]
);
diff --git a/ui/include/classes/parsers/CExpressionMacroFunctionParser.php b/ui/include/classes/parsers/CExpressionMacroFunctionParser.php
index 312f01ddabc..e1c74fc188f 100644
--- a/ui/include/classes/parsers/CExpressionMacroFunctionParser.php
+++ b/ui/include/classes/parsers/CExpressionMacroFunctionParser.php
@@ -30,7 +30,7 @@ class CExpressionMacroFunctionParser extends CParser {
protected $expression_macro_parser;
/**
- * @var CFunctionParser
+ * @var C10FunctionParser
*/
protected $function_parser;
@@ -39,7 +39,7 @@ class CExpressionMacroFunctionParser extends CParser {
*/
public function __construct() {
$this->expression_macro_parser = new CExpressionMacroParser();
- $this->function_parser = new CFunctionParser();
+ $this->function_parser = new C10FunctionParser();
}
/**
@@ -64,7 +64,7 @@ class CExpressionMacroFunctionParser extends CParser {
}
$p += $this->expression_macro_parser->getLength();
- if (!isset($source[$p]) || $source[$p] !== '.') {
+ if ($source[$p] !== '.') {
return CParser::PARSE_FAIL;
}
$p++;
@@ -82,6 +82,6 @@ class CExpressionMacroFunctionParser extends CParser {
$this->length = $p - $pos;
$this->match = substr($source, $pos, $this->length);
- return (isset($source[$pos + $this->length]) ? CParser::PARSE_SUCCESS_CONT : CParser::PARSE_SUCCESS);
+ return (isset($source[$p]) ? CParser::PARSE_SUCCESS_CONT : CParser::PARSE_SUCCESS);
}
}
diff --git a/ui/include/classes/parsers/CExpressionMacroParser.php b/ui/include/classes/parsers/CExpressionMacroParser.php
index 74687ddb3c9..2f35cb2c840 100644
--- a/ui/include/classes/parsers/CExpressionMacroParser.php
+++ b/ui/include/classes/parsers/CExpressionMacroParser.php
@@ -25,16 +25,23 @@
class CExpressionMacroParser extends CParser {
/**
- * @var CTriggerExpression
+ * @var CExpressionParser
*/
- protected $trigger_expression_parser;
+ private $expression_parser;
+
+ /**
+ * @var string
+ */
+ private $error = '';
/**
* Set up necessary parsers.
*/
public function __construct() {
- $this->trigger_expression_parser = new CTriggerExpression([
- 'host_macro' => ['{HOST.HOST}']
+ $this->expression_parser = new CExpressionParser([
+ 'lldmacros' => true,
+ 'host_macro_n' => true,
+ 'empty_host' => true
]);
}
@@ -47,28 +54,35 @@ class CExpressionMacroParser extends CParser {
public function parse($source, $pos = 0) {
$this->length = 0;
$this->match = '';
+ $this->error = '';
$p = $pos;
- if (!isset($source[$p]) || substr($source, $p, 2) !== '{?') {
- $this->errorPos($source, $p);
-
+ if (substr($source, $p, 2) !== '{?') {
return CParser::PARSE_FAIL;
}
$p += 2;
- $this->trigger_expression_parser->parse(substr($source, $p));
+ switch ($this->expression_parser->parse($source, $p)) {
+ case CParser::PARSE_SUCCESS_CONT:
+ $this->error = $this->expression_parser->getError();
+ break;
- if ($this->trigger_expression_parser->error_type !== CTriggerExpression::ERROR_UNPARSED_CONTENT) {
- $this->errorPos($source, $p + $this->trigger_expression_parser->error_pos);
+ case CParser::PARSE_FAIL:
+ $this->error = $this->expression_parser->getError();
+ return CParser::PARSE_FAIL;
+ }
+ $p += $this->expression_parser->getLength();;
- return CParser::PARSE_FAIL;
+ while (isset($source[$p]) && strpos(CExpressionParser::WHITESPACES, $source[$p]) !== false) {
+ $p++;
}
- $p += $this->trigger_expression_parser->error_pos;
- if (!isset($source[$p]) || $source[$p] !== '}') {
- $this->errorPos($source, $p);
+ if (!isset($source[$p])) {
+ $this->error = _('unexpected end of expression macro');
+ }
+ if (!isset($source[$p]) || $source[$p] !== '}') {
return CParser::PARSE_FAIL;
}
$p++;
@@ -76,6 +90,16 @@ class CExpressionMacroParser extends CParser {
$this->length = $p - $pos;
$this->match = substr($source, $pos, $this->length);
- return (isset($source[$pos + $this->length]) ? CParser::PARSE_SUCCESS_CONT : CParser::PARSE_SUCCESS);
+ return (isset($source[$p]) ? CParser::PARSE_SUCCESS_CONT : CParser::PARSE_SUCCESS);
}
+
+ /**
+ * Returns the error message if the expression macro is invalid.
+ *
+ * @return string
+ */
+ public function getError(): string {
+ return $this->error;
+ }
+
}
diff --git a/ui/include/classes/parsers/CExpressionParser.php b/ui/include/classes/parsers/CExpressionParser.php
new file mode 100644
index 00000000000..0c291b7b35b
--- /dev/null
+++ b/ui/include/classes/parsers/CExpressionParser.php
@@ -0,0 +1,867 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CExpressionParser extends CParser {
+
+ // For parsing of expressions.
+ private const STATE_AFTER_OPEN_BRACE = 1;
+ private const STATE_AFTER_BINARY_OPERATOR = 2;
+ private const STATE_AFTER_LOGICAL_OPERATOR = 3;
+ private const STATE_AFTER_NOT_OPERATOR = 4;
+ private const STATE_AFTER_UNARY_MINUS = 5;
+ private const STATE_AFTER_CLOSE_BRACE = 6;
+ private const STATE_AFTER_CONSTANT = 7;
+
+ // For parsing of math function parameters.
+ private const STATE_NEW = 1;
+ private const STATE_END = 2;
+ private const STATE_END_OF_PARAMS = 3;
+
+ private const MAX_MATH_FUNCTION_DEPTH = 32;
+
+ /**
+ * An error message if trigger expression is not valid
+ *
+ * @var string
+ */
+ private $error = '';
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'lldmacros' => false Enable low-level discovery macros usage in trigger expression.
+ * 'collapsed_expression' => false Short trigger expression.
+ * For example: {439} > {$MAX_THRESHOLD} or {439} < {$MIN_THRESHOLD}
+ * 'calculated' => false Parse calculated item formula instead of trigger expression.
+ * 'host_macro' => false Allow {HOST.HOST} macro as host name part in the query.
+ * 'host_macro_n' => false Allow {HOST.HOST} and {HOST.HOST<1-9>} macros as host name part in the query.
+ * 'empty_host' => false Allow empty hostname in the query string.
+ *
+ * @var array
+ */
+ private $options = [
+ 'lldmacros' => false,
+ 'collapsed_expression' => false,
+ 'calculated' => false,
+ 'host_macro' => false,
+ 'host_macro_n' => false,
+ 'empty_host' => false
+ ];
+
+ /**
+ * Object containing the results of parsing.
+ *
+ * @var null|CExpressionParserResult
+ */
+ private $result;
+
+ /**
+ * Chars that should be treated as spaces.
+ */
+ public const WHITESPACES = " \r\n\t";
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+
+ if ($this->options['collapsed_expression']
+ && ($this->options['host_macro'] || $this->options['host_macro_n'])) {
+ exit('Incompatible options.');
+ }
+ }
+
+ /**
+ * Parse an expression and set public variables $this->error, $this->result
+ *
+ * Examples:
+ * last(/Zabbix server/agent.ping,0) = 1 and {TRIGGER.VALUE} = {$MACRO}
+ *
+ * @param string $source
+ * @param int $pos
+ *
+ * @return int
+ */
+ public function parse($source, $pos = 0) {
+ // initializing local variables
+ $this->error = '';
+ $this->match = '';
+ $this->length = 0;
+
+ $p = $pos;
+ $tokens = [];
+ $parsed_pos = 0;
+
+ if (self::parseExpression($source, $p, $tokens, $this->options, $parsed_pos)) {
+ // Including trailing whitespaces as part of the expression.
+ if (preg_match('/^['.self::WHITESPACES.']+$/', substr($source, $p), $matches)) {
+ $p += strlen($matches[0]);
+ }
+ $len = $p - $pos;
+
+ $this->length = $len;
+ $this->match = substr($source, $pos, $len);
+
+ $this->result = new CExpressionParserResult();
+ $this->result->addTokens($tokens);
+ $this->result->pos = $pos;
+
+ if (isset($source[$p])) {
+ $this->error = _s('incorrect expression starting from "%1$s"', substr($source, $parsed_pos));
+
+ return self::PARSE_SUCCESS_CONT;
+ }
+
+ return self::PARSE_SUCCESS;
+ }
+
+ $this->error = _s('incorrect expression starting from "%1$s"', substr($source, $parsed_pos));
+
+ return self::PARSE_FAIL;
+ }
+
+ /**
+ * Parses an expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ * @param int $parsed_pos
+ * @param int $depth
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseExpression(string $source, int &$pos, array &$tokens, array $options,
+ int &$parsed_pos = null, int $depth = 0): bool {
+ $binary_operator_parser = new CSetParser(['<', '>', '<=', '>=', '+', '-', '/', '*', '=', '<>']);
+ $logical_operator_parser = new CSetParser(['and', 'or']);
+
+ if ($depth++ > self::MAX_MATH_FUNCTION_DEPTH) {
+ return false;
+ }
+
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $after_space = false;
+ $level = 0;
+ $p = $pos;
+ $_tokens = [];
+
+ while (isset($source[$p])) {
+ $char = $source[$p];
+
+ if (strpos(self::WHITESPACES, $char) !== false) {
+ $after_space = true;
+ $p++;
+ continue;
+ }
+
+ switch ($state) {
+ case self::STATE_AFTER_OPEN_BRACE:
+ switch ($char) {
+ case '-':
+ $state = self::STATE_AFTER_UNARY_MINUS;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ case '(':
+ $level++;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (self::parseNot($source, $p, $_tokens)) {
+ $state = self::STATE_AFTER_NOT_OPERATOR;
+ }
+ elseif (self::parseConstant($source, $p, $_tokens, $options, $depth)) {
+ $state = self::STATE_AFTER_CONSTANT;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_BINARY_OPERATOR:
+ switch ($char) {
+ case '-':
+ $state = self::STATE_AFTER_UNARY_MINUS;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (self::parseConstant($source, $p, $_tokens, $options, $depth)) {
+ $state = self::STATE_AFTER_CONSTANT;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ break;
+ }
+
+ if ($after_space && self::parseNot($source, $p, $_tokens)) {
+ $state = self::STATE_AFTER_NOT_OPERATOR;
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_LOGICAL_OPERATOR:
+ switch ($char) {
+ case '-':
+ if (!$after_space) {
+ break 3;
+ }
+ $state = self::STATE_AFTER_UNARY_MINUS;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (!$after_space) {
+ break 3;
+ }
+
+ if (self::parseNot($source, $p, $_tokens)) {
+ $state = self::STATE_AFTER_NOT_OPERATOR;
+ }
+ elseif (self::parseConstant($source, $p, $_tokens, $options, $depth)) {
+ $state = self::STATE_AFTER_CONSTANT;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_CLOSE_BRACE:
+ switch ($char) {
+ case ')':
+ if ($level == 0) {
+ break 3;
+ }
+ $level--;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ break;
+
+ default:
+ if (self::parseUsing($binary_operator_parser, $source, $p, $_tokens,
+ CExpressionParserResult::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_BINARY_OPERATOR;
+ break;
+ }
+
+ if (self::parseUsing($logical_operator_parser, $source, $p, $_tokens,
+ CExpressionParserResult::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_LOGICAL_OPERATOR;
+ break;
+ }
+ break 3;
+ }
+ break;
+
+ case self::STATE_AFTER_CONSTANT:
+ switch ($char) {
+ case ')':
+ if ($level == 0) {
+ break 3;
+ }
+ $level--;
+ $state = self::STATE_AFTER_CLOSE_BRACE;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ break;
+
+ default:
+ if (self::parseUsing($binary_operator_parser, $source, $p, $_tokens,
+ CExpressionParserResult::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_BINARY_OPERATOR;
+ break;
+ }
+
+ if ($after_space && self::parseUsing($logical_operator_parser, $source, $p, $_tokens,
+ CExpressionParserResult::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_LOGICAL_OPERATOR;
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_NOT_OPERATOR:
+ switch ($char) {
+ case '-':
+ if (!$after_space) {
+ break 3;
+ }
+ $state = self::STATE_AFTER_UNARY_MINUS;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (!$after_space) {
+ break 3;
+ }
+
+ if (self::parseConstant($source, $p, $_tokens, $options, $depth)) {
+ $state = self::STATE_AFTER_CONSTANT;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_UNARY_MINUS:
+ switch ($char) {
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (self::parseConstant($source, $p, $_tokens, $options, $depth)) {
+ $state = self::STATE_AFTER_CONSTANT;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+ }
+
+ $after_space = false;
+ $p++;
+ }
+
+ $parsed_pos = $p;
+
+ return (bool) $tokens;
+ }
+
+ /**
+ * Parse unary "not".
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ *
+ * @return bool
+ */
+ private static function parseNot(string $source, int &$pos, array &$tokens): bool {
+ if (substr($source, $pos, 3) !== 'not' || !isset($source[$pos + 3])
+ || strpos(self::WHITESPACES.'(', $source[$pos + 3]) === false) {
+ return false;
+ }
+
+ $tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => $pos,
+ 'match' => 'not',
+ 'length' => 3
+ ];
+ $pos += 2;
+
+ return true;
+ }
+
+ /**
+ * Parse the string using the given parser. If a match has been found, move the cursor to the last symbol of the
+ * matched string.
+ *
+ * @param CParser $parser
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param int $token_type
+ *
+ * @return bool
+ */
+ private static function parseUsing(CParser $parser, string $source, int &$pos, array &$tokens,
+ int $token_type): bool {
+ if ($parser->parse($source, $pos) == CParser::PARSE_FAIL) {
+ return false;
+ }
+
+ $tokens[] = [
+ 'type' => $token_type,
+ 'pos' => $pos,
+ 'match' => $parser->getMatch(),
+ 'length' => $parser->getLength()
+ ];
+ $pos += $parser->getLength() - 1;
+
+ return true;
+ }
+
+ /**
+ * Parses a constant in the expression.
+ *
+ * The constant can be:
+ * - function like func(<expression>)
+ * - function like func(/host/item,<params>)
+ * - floating point number; can be with suffix [KMGTsmhdw]
+ * - string
+ * - macro like {TRIGGER.VALUE}
+ * - user macro like {$MACRO}
+ * - LLD macro like {#LLD}
+ * - LLD macro with function like {{#LLD}.func())}
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ * @param int $depth
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseConstant(string $source, int &$pos, array &$tokens, array $options, int $depth): bool {
+ $user_macro_parser = new CUserMacroParser();
+
+ if (self::parseNumber($source, $pos, $tokens) || self::parseString($source, $pos, $tokens)
+ || self::parseUsing($user_macro_parser, $source, $pos, $tokens,
+ CExpressionParserResult::TOKEN_TYPE_USER_MACRO)) {
+ return true;
+ }
+
+ if (!$options['calculated']) {
+ $macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
+
+ if (self::parseUsing($macro_parser, $source, $pos, $tokens, CExpressionParserResult::TOKEN_TYPE_MACRO)) {
+ return true;
+ }
+ }
+
+ if ($options['collapsed_expression']) {
+ $functionid_parser = new CFunctionIdParser();
+
+ if (self::parseUsing($functionid_parser, $source, $pos, $tokens,
+ CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO)) {
+ return true;
+ }
+ }
+ elseif (self::parseHistFunction($source, $pos, $tokens, $options)) {
+ return true;
+ }
+
+ if (self::parseMathFunction($source, $pos, $tokens, $options, $depth)) {
+ return true;
+ }
+
+ if ($options['lldmacros']) {
+ $lld_macro_parser = new CLLDMacroParser();
+
+ if (self::parseUsing($lld_macro_parser, $source, $pos, $tokens,
+ CExpressionParserResult::TOKEN_TYPE_LLD_MACRO)) {
+ return true;
+ }
+
+ $lld_macro_function_parser = new CLLDMacroFunctionParser();
+
+ if (self::parseUsing($lld_macro_function_parser, $source, $pos, $tokens,
+ CExpressionParserResult::TOKEN_TYPE_LLD_MACRO)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parses a historical function constant in the expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseHistFunction(string $source, int &$pos, array &$tokens, array $options): bool {
+ $hist_function_parser = new CHistFunctionParser([
+ 'usermacros' => true,
+ 'lldmacros' => $options['lldmacros'],
+ 'calculated' => $options['calculated'],
+ 'host_macro' => $options['host_macro'],
+ 'host_macro_n' => $options['host_macro_n'],
+ 'empty_host' => $options['empty_host']
+ ]);
+
+ if ($hist_function_parser->parse($source, $pos) == CParser::PARSE_FAIL) {
+ return false;
+ }
+
+ $len = $hist_function_parser->getLength();
+ $tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION,
+ 'pos' => $pos,
+ 'match' => $hist_function_parser->getMatch(),
+ 'length' => $len,
+ 'data' => [
+ 'function' => $hist_function_parser->getFunction(),
+ 'parameters' => $hist_function_parser->getParameters()
+ ]
+ ];
+ $pos += $len - 1;
+
+ return true;
+ }
+
+ /**
+ * Parses a math function constant in the expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ * @param int $depth
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseMathFunction(string $source, int &$pos, array &$tokens, array $options,
+ int $depth): bool {
+ $p = $pos;
+
+ if (!preg_match('/^([a-z]+)\(/', substr($source, $p), $matches)) {
+ return false;
+ }
+
+ $p += strlen($matches[0]);
+ $p2 = $p - 1;
+ $_tokens = [];
+ $state = self::STATE_NEW;
+
+ while (isset($source[$p])) {
+ switch ($state) {
+ case self::STATE_NEW:
+ switch ($source[$p]) {
+ case ' ':
+ break;
+
+ case ')':
+ if (!$_tokens) {
+ $state = self::STATE_END_OF_PARAMS;
+ break;
+ }
+ break 3;
+
+ default:
+ $_p = $p;
+ $expression_tokens = [];
+ $parsed_pos = 0;
+
+ if (!self::parseExpression($source, $_p, $expression_tokens, $options, $parsed_pos,
+ $depth)) {
+ break 3;
+ }
+
+ $len = $_p - $p;
+ $_tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => $p,
+ 'match' => substr($source, $p, $len),
+ 'length' => $len,
+ 'data' => [
+ 'tokens' => $expression_tokens
+ ]
+ ];
+ $p = $_p - 1;
+ $state = self::STATE_END;
+ }
+ break;
+
+ case self::STATE_END:
+ switch ($source[$p]) {
+ case ' ':
+ break;
+
+ case ')':
+ $state = self::STATE_END_OF_PARAMS;
+ break;
+
+ case ',':
+ $state = self::STATE_NEW;
+ break;
+
+ default:
+ break 3;
+ }
+ break;
+
+ case self::STATE_END_OF_PARAMS:
+ break 2;
+ }
+
+ $p++;
+ }
+
+ if ($state != self::STATE_END_OF_PARAMS) {
+ return false;
+ }
+
+ $len = $p - $pos;
+ $tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ 'pos' => $pos,
+ 'match' => substr($source, $pos, $len),
+ 'length' => $len,
+ 'data' => [
+ 'function' => $matches[1],
+ 'parameters' => $_tokens
+ ]
+ ];
+ $pos += $len - 1;
+
+ return true;
+ }
+
+ /**
+ * Parses a number constant in the expression and moves a current position on a last symbol of the number.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ *
+ * @return bool returns true if parsed successfully, false otherwise
+ */
+ private static function parseNumber(string $source, int &$pos, array &$tokens): bool {
+ $number_parser = new CNumberParser(['with_minus' => false, 'with_suffix' => true]);
+
+ if ($number_parser->parse($source, $pos) == CParser::PARSE_FAIL) {
+ return false;
+ }
+
+ $value = $number_parser->calcValue();
+ if (abs($value) == INF) {
+ return false;
+ }
+
+ $len = $number_parser->getLength();
+ $tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => $pos,
+ 'match' => $number_parser->getMatch(),
+ 'length' => $len,
+ 'data' => ['suffix' => $number_parser->getSuffix()]
+ ];
+ $pos += $len - 1;
+
+ return true;
+ }
+
+ /**
+ * Parses a quoted string constant in the expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ *
+ * @return bool returns true if parsed successfully, false otherwise
+ */
+ private static function parseString(string $source, int &$pos, array &$tokens): bool {
+ if (!preg_match('/^"([^"\\\\]|\\\\["\\\\])*"/', substr($source, $pos), $matches)) {
+ return false;
+ }
+
+ $len = strlen($matches[0]);
+ $tokens[] = [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_STRING,
+ 'pos' => $pos,
+ 'match' => $matches[0],
+ 'length' => $len
+ ];
+ $pos += $len - 1;
+
+ return true;
+ }
+
+ /**
+ * Unquoting quoted string $value.
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function unquoteString(string $value): string {
+ return strtr(substr($value, 1, -1), ['\\"' => '"', '\\\\' => '\\']);
+ }
+
+ /**
+ * Quoting $value if it contains a non numeric value.
+ *
+ * @param string $value
+ * @param bool $allow_macros
+ * @param bool $force
+ *
+ * @return string
+ */
+ public static function quoteString(string $value, bool $allow_macros = true, bool $force = false): string {
+ if (!$force) {
+ $number_parser = new CNumberParser(['with_suffix' => true]);
+
+ if ($number_parser->parse($value) == CParser::PARSE_SUCCESS) {
+ return $value;
+ }
+
+ if ($allow_macros) {
+ $user_macro_parser = new CUserMacroParser();
+ $macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
+ $lld_macro_parser = new CLLDMacroParser();
+ $lld_macro_function_parser = new CLLDMacroFunctionParser;
+
+ if ($user_macro_parser->parse($value) == CParser::PARSE_SUCCESS
+ || $macro_parser->parse($value) == CParser::PARSE_SUCCESS
+ || $lld_macro_parser->parse($value) == CParser::PARSE_SUCCESS
+ || $lld_macro_function_parser->parse($value) == CParser::PARSE_SUCCESS) {
+ return $value;
+ }
+ }
+ }
+
+ return '"'.strtr($value, ['\\' => '\\\\', '"' => '\\"']).'"';
+ }
+
+ /**
+ * Returns an expression parser result.
+ *
+ * @return null|CExpressionParserResult
+ */
+ public function getResult(): ?CExpressionParserResult {
+ return $this->result;
+ }
+
+ /**
+ * Returns a friendly error message or empty string if expression was parsed successfully.
+ *
+ * @return string
+ */
+ public function getError(): string {
+ return $this->error;
+ }
+}
diff --git a/ui/include/classes/parsers/CFilterParser.php b/ui/include/classes/parsers/CFilterParser.php
new file mode 100644
index 00000000000..2893626546f
--- /dev/null
+++ b/ui/include/classes/parsers/CFilterParser.php
@@ -0,0 +1,530 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CFilterParser extends CParser {
+
+ // For parsing of filter expressions.
+ private const STATE_AFTER_OPEN_BRACE = 1;
+ private const STATE_AFTER_LOGICAL_OPERATOR = 2;
+ private const STATE_AFTER_NOT_OPERATOR = 3;
+ private const STATE_AFTER_CLOSE_BRACE = 4;
+ private const STATE_AFTER_PAIR = 5;
+
+ // Token types.
+ public const TOKEN_TYPE_OPEN_BRACE = 0;
+ public const TOKEN_TYPE_CLOSE_BRACE = 1;
+ public const TOKEN_TYPE_OPERATOR = 2;
+ public const TOKEN_TYPE_KEYWORD = 3;
+ public const TOKEN_TYPE_USER_MACRO = 4;
+ public const TOKEN_TYPE_LLD_MACRO = 5;
+ public const TOKEN_TYPE_STRING = 6;
+
+ /**
+ * Chars that should be treated as spaces.
+ */
+ public const WHITESPACES = " \r\n\t";
+
+ /**
+ * Array of tokens.
+ *
+ * @var array
+ */
+ protected $tokens = [];
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'usermacros' => false Enable user macros usage in filter expression.
+ * 'lldmacros' => false Enable low-level discovery macros usage in filter expression.
+ *
+ * @var array
+ */
+ private $options = [
+ 'usermacros' => false,
+ 'lldmacros' => false
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+ }
+
+ /**
+ * Parse a filter expression.
+ *
+ * Examples:
+ * ?[tag = "Service:MySQL" and group = "Database servers"]
+ *
+ * @param string $expression
+ * @param int $pos
+ *
+ * @return int
+ */
+ public function parse($source, $pos = 0) {
+ // initializing local variables
+ $this->match = '';
+ $this->length = 0;
+
+ $p = $pos;
+ $tokens = [];
+
+ if (substr($source, $p, 2) !== '?[') {
+ return self::PARSE_FAIL;
+ }
+ $p += 2;
+
+ if (!self::parseExpression($source, $p, $tokens, $this->options)) {
+ return self::PARSE_FAIL;
+ }
+
+ if (!isset($source[$p]) || $source[$p] !== ']') {
+ return self::PARSE_FAIL;
+ }
+ $p++;
+
+ $len = $p - $pos;
+
+ $this->length = $len;
+ $this->match = substr($source, $pos, $len);
+ $this->tokens = $tokens;
+
+ return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ }
+
+ /**
+ * Parses an expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseExpression(string $source, int &$pos, array &$tokens, array $options): bool {
+ $logical_operator_parser = new CSetParser(['and', 'or']);
+ $not_operator_parser = new CSetParser(['not']);
+
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $after_space = false;
+ $level = 0;
+ $p = $pos;
+ $_tokens = [];
+
+ while (isset($source[$p])) {
+ $char = $source[$p];
+
+ if (strpos(self::WHITESPACES, $char) !== false) {
+ $after_space = true;
+ $p++;
+ continue;
+ }
+
+ switch ($state) {
+ case self::STATE_AFTER_OPEN_BRACE:
+ switch ($char) {
+ case '(':
+ $level++;
+ $_tokens[] = [
+ 'type' => self::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (self::parseUsing($not_operator_parser, $source, $p, $_tokens,
+ self::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_NOT_OPERATOR;
+ }
+ elseif (self::parsePair($source, $p, $_tokens, $options)) {
+ $state = self::STATE_AFTER_PAIR;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_LOGICAL_OPERATOR:
+ switch ($char) {
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => self::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (!$after_space) {
+ break 3;
+ }
+
+ if (self::parseUsing($not_operator_parser, $source, $p, $_tokens,
+ self::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_NOT_OPERATOR;
+ }
+ elseif (self::parsePair($source, $p, $_tokens, $options)) {
+ $state = self::STATE_AFTER_PAIR;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_CLOSE_BRACE:
+ switch ($char) {
+ case ')':
+ if ($level == 0) {
+ break 3;
+ }
+ $level--;
+ $_tokens[] = [
+ 'type' => self::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ break;
+
+ default:
+ if (self::parseUsing($logical_operator_parser, $source, $p, $_tokens,
+ self::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_LOGICAL_OPERATOR;
+ break;
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_PAIR:
+ switch ($char) {
+ case ')':
+ if ($level == 0) {
+ break 3;
+ }
+ $level--;
+ $state = self::STATE_AFTER_CLOSE_BRACE;
+ $_tokens[] = [
+ 'type' => self::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ break;
+
+ default:
+ if ($after_space && self::parseUsing($logical_operator_parser, $source, $p, $_tokens,
+ self::TOKEN_TYPE_OPERATOR)) {
+ $state = self::STATE_AFTER_LOGICAL_OPERATOR;
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+
+ case self::STATE_AFTER_NOT_OPERATOR:
+ switch ($char) {
+ case '(':
+ $level++;
+ $state = self::STATE_AFTER_OPEN_BRACE;
+ $_tokens[] = [
+ 'type' => self::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => $p,
+ 'match' => $char,
+ 'length' => 1
+ ];
+ break;
+
+ default:
+ if (!$after_space) {
+ break 3;
+ }
+
+ if (self::parsePair($source, $p, $_tokens, $options)) {
+ $state = self::STATE_AFTER_PAIR;
+
+ if ($level == 0) {
+ $pos = $p + 1;
+ $tokens = $_tokens;
+ }
+ }
+ else {
+ break 3;
+ }
+ }
+ break;
+ }
+
+ $after_space = false;
+ $p++;
+ }
+
+ if ($tokens) {
+ // Including trailing whitespaces as part of the expression.
+ while (isset($source[$pos]) && strpos(self::WHITESPACES, $source[$pos]) !== false) {
+ $pos++;
+ }
+ }
+
+ return (bool) $tokens;
+ }
+
+ /**
+ * Parses a constant in the expression.
+ *
+ * The pair can be:
+ * - <keyword> <operator> <quoted string>
+ * - <quoted string> <operator> <keyword>
+ *
+ * <operator> - =|<>
+ * <keyword> - tag|group
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parsePair(string $source, int &$pos, array &$tokens, array $options): bool {
+ $keyword_parser = new CSetParser(['tag', 'group']);
+ $binary_operator_parser = new CSetParser(['=', '<>']);
+
+ $p = $pos;
+ $_tokens = [];
+ $keywords = 0;
+
+ if (self::parseUsing($keyword_parser, $source, $p, $_tokens, self::TOKEN_TYPE_KEYWORD)) {
+ $keywords++;
+ }
+ elseif (!self::parseConstant($source, $p, $_tokens, $options)) {
+ return false;
+ }
+ $p++;
+
+ while (isset($source[$p]) && strpos(self::WHITESPACES, $source[$p]) !== false) {
+ $p++;
+ }
+
+ if (!self::parseUsing($binary_operator_parser, $source, $p, $_tokens, self::TOKEN_TYPE_OPERATOR)) {
+ return false;
+ }
+ $p++;
+
+ while (isset($source[$p]) && strpos(self::WHITESPACES, $source[$p]) !== false) {
+ $p++;
+ }
+
+ if (self::parseUsing($keyword_parser, $source, $p, $_tokens, self::TOKEN_TYPE_KEYWORD)) {
+ $keywords++;
+ }
+ else if (!self::parseConstant($source, $p, $_tokens, $options)) {
+ return false;
+ }
+ $p++;
+
+ if ($keywords > 1) {
+ return false;
+ }
+
+ $pos = $p - 1;
+ $tokens = array_merge($tokens, $_tokens);
+
+ return true;
+ }
+
+
+ /**
+ * Parses a constant in the expression.
+ *
+ * The constant can be:
+ * - string
+ * - user macro like {$MACRO}
+ * - LLD macro like {#LLD}
+ * - LLD macro with function like {{#LLD}.func())}
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param array $options
+ *
+ * @return bool Returns true if parsed successfully, false otherwise.
+ */
+ private static function parseConstant(string $source, int &$pos, array &$tokens, array $options): bool {
+ if (self::parseString($source, $pos, $tokens)) {
+ return true;
+ }
+
+ if ($options['usermacros'] && self::parseUsing(new CUserMacroParser(), $source, $pos, $tokens,
+ self::TOKEN_TYPE_USER_MACRO)) {
+ return true;
+ }
+
+ if ($options['lldmacros'] && self::parseUsing(new CLLDMacroParser(), $source, $pos, $tokens,
+ self::TOKEN_TYPE_LLD_MACRO)) {
+ return true;
+ }
+
+ if ($options['lldmacros'] && self::parseUsing(new CLLDMacroFunctionParser(), $source, $pos, $tokens,
+ self::TOKEN_TYPE_LLD_MACRO)) {
+ return true;
+ }
+
+ return false;
+ }
+ /**
+ * Parses a quoted string constant in the expression.
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ *
+ * @return bool returns true if parsed successfully, false otherwise
+ */
+ private static function parseString(string $source, int &$pos, array &$tokens): bool {
+ if (!preg_match('/^"([^"\\\\]|\\\\["\\\\])*"/', substr($source, $pos), $matches)) {
+ return false;
+ }
+
+ $len = strlen($matches[0]);
+ $tokens[] = [
+ 'type' => self::TOKEN_TYPE_STRING,
+ 'pos' => $pos,
+ 'match' => $matches[0],
+ 'length' => $len
+ ];
+ $pos += $len - 1;
+
+ return true;
+ }
+
+ /**
+ * Parse the string using the given parser. If a match has been found, move the cursor to the last symbol of the
+ * matched string.
+ *
+ * @param CParser $parser
+ * @param string $source
+ * @param int $pos
+ * @param array $tokens
+ * @param int $token_type
+ *
+ * @return bool
+ */
+ private static function parseUsing(CParser $parser, string $source, int &$pos, array &$tokens,
+ int $token_type): bool {
+ if ($parser->parse($source, $pos) == CParser::PARSE_FAIL) {
+ return false;
+ }
+
+ $tokens[] = [
+ 'type' => $token_type,
+ 'pos' => $pos,
+ 'match' => $parser->getMatch(),
+ 'length' => $parser->getLength()
+ ];
+ $pos += $parser->getLength() - 1;
+
+ return true;
+ }
+
+ /**
+ * Return the expression tokens.
+ *
+ * @return array
+ */
+ public function getTokens(): array {
+ return $this->tokens;
+ }
+
+ /**
+ * Returns tokens of the given types.
+ *
+ * @param array $types
+ *
+ * @return array
+ */
+ public function getTokensOfTypes(array $types): array {
+ $result = [];
+
+ foreach ($this->tokens as $token) {
+ if (in_array($token['type'], $types)) {
+ $result[] = $token;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Unquoting quoted string $value.
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function unquoteString(string $value): string {
+ return strtr(substr($value, 1, -1), ['\\"' => '"', '\\\\' => '\\']);
+ }
+
+ /**
+ * Quoting string $value.
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function quoteString(string $value): string {
+ return '"'.strtr($value, ['\\' => '\\\\', '"' => '\\"']).'"';
+ }
+}
diff --git a/ui/include/classes/parsers/CHistFunctionParser.php b/ui/include/classes/parsers/CHistFunctionParser.php
new file mode 100644
index 00000000000..231988232c9
--- /dev/null
+++ b/ui/include/classes/parsers/CHistFunctionParser.php
@@ -0,0 +1,402 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class is used to validate and parse a function.
+ */
+class CHistFunctionParser extends CParser {
+
+ protected const STATE_NEW = 0;
+ protected const STATE_END = 1;
+ protected const STATE_QUOTED = 3;
+ protected const STATE_END_OF_PARAMS = 4;
+
+ public const PARAM_TYPE_QUERY = 0;
+ public const PARAM_TYPE_PERIOD = 1;
+ public const PARAM_TYPE_QUOTED = 2;
+ public const PARAM_TYPE_UNQUOTED = 3;
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'usermacros' => false Enable user macros usage in function parameters.
+ * 'lldmacros' => false Enable low-level discovery macros usage in function parameters.
+ * 'host_macro' => false Allow {HOST.HOST} macro as host name part in the query.
+ * 'host_macro_n' => false Allow {HOST.HOST} and {HOST.HOST<1-9>} macros as host name part in the query.
+ * 'empty_host' => false Allow empty hostname in the query string.
+ *
+ * @var array
+ */
+ private $options = [
+ 'usermacros' => false,
+ 'lldmacros' => false,
+ 'calculated' => false,
+ 'host_macro' => false,
+ 'host_macro_n' => false,
+ 'empty_host' => false
+ ];
+
+ private $query_parser;
+ private $period_parser;
+ private $user_macro_parser;
+ private $lld_macro_parser;
+ private $lld_macro_function_parser;
+ private $number_parser;
+
+ /**
+ * Parsed function name.
+ *
+ * @var string
+ */
+ private $function = '';
+
+ /**
+ * The list of the parsed function parameters.
+ *
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+
+ $this->query_parser = new CQueryParser([
+ 'usermacros' => $this->options['usermacros'],
+ 'lldmacros' => $this->options['lldmacros'],
+ 'calculated' => $this->options['calculated'],
+ 'host_macro' => $this->options['host_macro'],
+ 'host_macro_n' => $this->options['host_macro_n'],
+ 'empty_host' => $this->options['empty_host']
+ ]);
+ $this->period_parser = new CPeriodParser([
+ 'usermacros' => $this->options['usermacros'],
+ 'lldmacros' => $this->options['lldmacros']
+ ]);
+ if ($this->options['usermacros']) {
+ $this->user_macro_parser = new CUserMacroParser();
+ }
+ if ($this->options['lldmacros']) {
+ $this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
+ }
+ $this->number_parser = new CNumberParser([
+ 'with_minus' => true,
+ 'with_suffix' => true
+ ]);
+ }
+
+ /**
+ * Parse a function and parameters and put them into $this->params_raw array.
+ *
+ * @param string $source
+ * @param int $pos
+ */
+ public function parse($source, $pos = 0): int {
+ $this->length = 0;
+ $this->match = '';
+ $this->function = '';
+
+ $p = $pos;
+
+ if (!preg_match('/^([a-z_]+)\(/', substr($source, $p), $matches)) {
+ return self::PARSE_FAIL;
+ }
+
+ $p += strlen($matches[0]);
+ $p2 = $p - 1;
+
+ $parameters = [];
+ if (!$this->parseFunctionParameters($source, $p, $parameters)) {
+ return self::PARSE_FAIL;
+ }
+
+ $params_raw['raw'] = substr($source, $p2, $p - $p2);
+
+ $this->length = $p - $pos;
+ $this->match = substr($source, $pos, $this->length);
+ $this->function = $matches[1];
+ $this->parameters = $parameters;
+
+ return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ }
+
+ /**
+ * @param string $source
+ * @param int $pos
+ * @param array $parameters
+ *
+ * @return bool
+ */
+ protected function parseFunctionParameters(string $source, int &$pos, array &$parameters): bool {
+ $p = $pos;
+
+ $_parameters = [];
+ $state = self::STATE_NEW;
+ $num = 0;
+
+ // The list of parsers for unquoted parameters.
+ $parsers = [$this->number_parser];
+ if ($this->options['usermacros']) {
+ $parsers[] = $this->user_macro_parser;
+ }
+ if ($this->options['lldmacros']) {
+ $parsers[] = $this->lld_macro_parser;
+ $parsers[] = $this->lld_macro_function_parser;
+ }
+
+ while (isset($source[$p])) {
+ switch ($state) {
+ // a new parameter started
+ case self::STATE_NEW:
+ if ($source[$p] !== ' ') {
+ if ($num == 0) {
+ if ($this->query_parser->parse($source, $p) != CParser::PARSE_FAIL) {
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_QUERY,
+ 'pos' => $p,
+ 'match' => $this->query_parser->getMatch(),
+ 'length' => $this->query_parser->getLength(),
+ 'data' => [
+ 'host' => $this->query_parser->getHost(),
+ 'item' => $this->query_parser->getItem(),
+ 'filter' => $this->query_parser->getFilter()
+ ]
+ ];
+ $p += $this->query_parser->getLength() - 1;
+ $state = self::STATE_END;
+ }
+ else {
+ break 2;
+ }
+ }
+ elseif ($num == 1) {
+ switch ($source[$p]) {
+ case ',':
+ $_parameters[$num++] = [
+ 'type' => self::PARAM_TYPE_UNQUOTED,
+ 'pos' => $p,
+ 'match' => '',
+ 'length' => 0
+ ];
+ break;
+
+ case ')':
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_UNQUOTED,
+ 'pos' => $p,
+ 'match' => '',
+ 'length' => 0
+ ];
+ $state = self::STATE_END_OF_PARAMS;
+ break;
+
+ case '"':
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_QUOTED,
+ 'pos' => $p,
+ 'match' => $source[$p],
+ 'length' => 1
+ ];
+ $state = self::STATE_QUOTED;
+ break;
+
+ default:
+ if ($this->period_parser->parse($source, $p) != CParser::PARSE_FAIL) {
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_PERIOD,
+ 'pos' => $p,
+ 'match' => $this->period_parser->getMatch(),
+ 'length' => $this->period_parser->getLength(),
+ 'data' => [
+ 'sec_num' => $this->period_parser->getSecNum(),
+ 'time_shift' => $this->period_parser->getTimeshift()
+ ]
+ ];
+ $p += $this->period_parser->getLength() - 1;
+ $state = self::STATE_END;
+ }
+ else {
+ break 3;
+ }
+ }
+ }
+ else {
+ switch ($source[$p]) {
+ case ',':
+ $_parameters[$num++] = [
+ 'type' => self::PARAM_TYPE_UNQUOTED,
+ 'pos' => $p,
+ 'match' => '',
+ 'length' => 0
+ ];
+ break;
+
+ case ')':
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_UNQUOTED,
+ 'pos' => $p,
+ 'match' => '',
+ 'length' => 0
+ ];
+ $state = self::STATE_END_OF_PARAMS;
+ break;
+
+ case '"':
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_QUOTED,
+ 'pos' => $p,
+ 'match' => $source[$p],
+ 'length' => 1
+ ];
+ $state = self::STATE_QUOTED;
+ break;
+
+ default:
+ foreach ($parsers as $parser) {
+ if ($parser->parse($source, $p) != CParser::PARSE_FAIL) {
+ $_parameters[$num] = [
+ 'type' => self::PARAM_TYPE_UNQUOTED,
+ 'pos' => $p,
+ 'match' => $parser->getMatch(),
+ 'length' => $parser->getLength()
+ ];
+
+ $p += $parser->getLength() - 1;
+ $state = self::STATE_END;
+ }
+ }
+
+ if ($state != self::STATE_END) {
+ break 3;
+ }
+ }
+ }
+ }
+ break;
+
+ // end of parameter
+ case self::STATE_END:
+ switch ($source[$p]) {
+ case ' ':
+ break;
+
+ case ',':
+ $state = self::STATE_NEW;
+ $num++;
+ break;
+
+ case ')':
+ $state = self::STATE_END_OF_PARAMS;
+ break;
+
+ default:
+ break 3;
+ }
+ break;
+
+ // a quoted parameter
+ case self::STATE_QUOTED:
+ $_parameters[$num]['match'] .= $source[$p];
+ $_parameters[$num]['length']++;
+
+ if ($source[$p] === '"' && $source[$p - 1] !== '\\') {
+ $state = self::STATE_END;
+ }
+ break;
+
+ // end of parameters
+ case self::STATE_END_OF_PARAMS:
+ break 2;
+ }
+
+ $p++;
+ }
+
+ if ($state == self::STATE_END_OF_PARAMS) {
+ $parameters = $_parameters;
+ $pos = $p;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the left part of the function without parameters.
+ *
+ * @return string
+ */
+ public function getFunction(): string {
+ return $this->function;
+ }
+
+ /**
+ * Returns the parameters of the function.
+ *
+ * @return array
+ */
+ public function getParameters(): array {
+ return $this->parameters;
+ }
+
+ /*
+ * Unquotes special symbols in the parameter.
+ *
+ * @param string $param
+ *
+ * @return string
+ */
+ public static function unquoteParam(string $param): string {
+ $unquoted = '';
+
+ for ($p = 1; isset($param[$p]); $p++) {
+ if ($param[$p] === '\\' && $param[$p + 1] === '"') {
+ continue;
+ }
+
+ $unquoted .= $param[$p];
+ }
+
+ return substr($unquoted, 0, -1);
+ }
+
+ /**
+ * Returns an unquoted parameter.
+ *
+ * @param int $n The number of the requested parameter.
+ *
+ * @return string|null
+ */
+ public function getParam(int $num): ?string {
+ if (!array_key_exists($num, $this->parameters)) {
+ return null;
+ }
+
+ $param = $this->parameters[$num];
+
+ return ($param['type'] == self::PARAM_TYPE_QUOTED) ? self::unquoteParam($param['match']) : $param['match'];
+ }
+}
diff --git a/ui/include/classes/parsers/CLLDMacroFunctionParser.php b/ui/include/classes/parsers/CLLDMacroFunctionParser.php
index fa0843ac29c..db6807763ab 100644
--- a/ui/include/classes/parsers/CLLDMacroFunctionParser.php
+++ b/ui/include/classes/parsers/CLLDMacroFunctionParser.php
@@ -34,7 +34,7 @@ class CLLDMacroFunctionParser extends CParser {
/**
* Parser for trigger functions.
*
- * @var CFunctionParser
+ * @var C10FunctionParser
*/
private $function_parser;
@@ -43,7 +43,7 @@ class CLLDMacroFunctionParser extends CParser {
*/
public function __construct() {
$this->lld_macro_parser = new CLLDMacroParser();
- $this->function_parser = new CFunctionParser();
+ $this->function_parser = new C10FunctionParser();
}
/**
diff --git a/ui/include/classes/parsers/CMacroFunctionParser.php b/ui/include/classes/parsers/CMacroFunctionParser.php
index 85280f91f42..af0db537742 100644
--- a/ui/include/classes/parsers/CMacroFunctionParser.php
+++ b/ui/include/classes/parsers/CMacroFunctionParser.php
@@ -34,7 +34,7 @@ class CMacroFunctionParser extends CParser {
/**
* Parser for trigger functions.
*
- * @var CFunctionParser
+ * @var C10FunctionParser
*/
private $function_parser;
@@ -45,7 +45,7 @@ class CMacroFunctionParser extends CParser {
*/
public function __construct(array $options) {
$this->macro_parser = new CMacroParser($options);
- $this->function_parser = new CFunctionParser();
+ $this->function_parser = new C10FunctionParser();
}
/**
diff --git a/ui/include/classes/parsers/CParser.php b/ui/include/classes/parsers/CParser.php
index 3d907e04264..f30ab64160d 100644
--- a/ui/include/classes/parsers/CParser.php
+++ b/ui/include/classes/parsers/CParser.php
@@ -18,6 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
abstract class CParser {
const PARSE_FAIL = -1;
@@ -70,13 +71,13 @@ abstract class CParser {
if ($this->error_source === false) {
return '';
}
- else if (!isset($this->error_source[$this->error_pos])) {
+
+ if (!isset($this->error_source[$this->error_pos])) {
return ($this->error_pos == 0) ? $this->error_msgs['empty'] : $this->error_msgs['unexpected_end'];
}
- else {
- // The error message is prepared here to avoid extra calculations, if error message is not used.
- return $this->errorPosMessage($this->error_source, $this->error_pos);
- }
+
+ // The error message is prepared here to avoid extra calculations if the error message is not used.
+ return $this->errorPosMessage($this->error_source, $this->error_pos);
}
/**
diff --git a/ui/include/classes/parsers/CPeriodParser.php b/ui/include/classes/parsers/CPeriodParser.php
new file mode 100644
index 00000000000..e66d2d86356
--- /dev/null
+++ b/ui/include/classes/parsers/CPeriodParser.php
@@ -0,0 +1,131 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class is used to parse <sec|#num>:<time_shift> trigger parameter.
+ */
+class CPeriodParser extends CParser {
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'usermacros' => false Enable user macros usage in the periods.
+ * 'lldmacros' => false Enable low-level discovery macros usage in the periods.
+ *
+ * @var array
+ */
+ protected $options = [
+ 'usermacros' => false,
+ 'lldmacros' => false
+ ];
+
+ /**
+ * @var string
+ */
+ private $sec_num = '';
+
+ /**
+ * @var string
+ */
+ private $timeshift = '';
+
+ /**
+ * @param array $options
+ * @param bool $options['lldmacros']
+ */
+ public function __construct(array $options) {
+ $this->options = $options + $this->options;
+
+ $this->simple_interval_parser = new CSimpleIntervalParser([
+ 'usermacros' => $this->options['usermacros'],
+ 'lldmacros' => $this->options['lldmacros'],
+ 'with_year' => true
+ ]);
+ $this->relative_time_parser = new CRelativeTimeParser([
+ 'usermacros' => $this->options['usermacros'],
+ 'lldmacros' => $this->options['lldmacros']
+ ]);
+ }
+
+ /**
+ * Parse period.
+ *
+ * @param string $source
+ * @param int $pos
+ *
+ * @return int
+ */
+ public function parse($source, $pos = 0): int {
+ $this->match = '';
+ $this->length = 0;
+ $this->sec_num = '';
+ $this->timeshift = '';
+
+ $p = $pos;
+ $sec_num = '';
+ $timeshift = '';
+
+ if (preg_match('/^#[0-9]+/', substr($source, $p), $matches)) {
+ $sec_num = $matches[0];
+ $p += strlen($matches[0]);
+ }
+ elseif ($this->simple_interval_parser->parse($source, $p) !== self::PARSE_FAIL) {
+ $sec_num = $this->simple_interval_parser->match;
+ $p += $this->simple_interval_parser->length;
+ }
+ else {
+ return self::PARSE_FAIL;
+ }
+
+ if (isset($source[$p]) && $source[$p] === ':') {
+ if ($this->relative_time_parser->parse($source, $p + 1) !== self::PARSE_FAIL) {
+ $timeshift = $this->relative_time_parser->match;
+ $p += $this->relative_time_parser->length + 1;
+ }
+ }
+
+ $this->length = $p - $pos;
+ $this->match = substr($source, $pos, $this->length);
+ $this->sec_num = $sec_num;
+ $this->timeshift = $timeshift;
+
+ return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ }
+
+ /**
+ * Returns the first part of the period.
+ *
+ * @return string
+ */
+ public function getSecNum(): string {
+ return $this->sec_num;
+ }
+
+ /**
+ * Returns the second part of the period.
+ *
+ * @return string
+ */
+ public function getTimeshift(): string {
+ return $this->timeshift;
+ }
+}
diff --git a/ui/include/classes/parsers/CQueryParser.php b/ui/include/classes/parsers/CQueryParser.php
new file mode 100644
index 00000000000..db139135492
--- /dev/null
+++ b/ui/include/classes/parsers/CQueryParser.php
@@ -0,0 +1,208 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class is used to parse a query in trigger function in form of "/host/key".
+ */
+class CQueryParser extends CParser {
+
+ // Wildcard character to define host or item key.
+ const HOST_ITEMKEY_WILDCARD = '*';
+
+ private $host_name_parser;
+ private $host_macro_parser;
+ private $item_key_parser;
+ private $filter_parser;
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'usermacros' => false Enable user macros usage in filter expression.
+ * 'lldmacros' => false Enable low-level discovery macros usage in filter expression.
+ * 'calculated' => false Allow wildcards to be used in place of hostname and item key. Allow filter expression
+ * for item.
+ * 'host_macro' => false Allow {HOST.HOST} macro as host name part in the query.
+ * 'host_macro_n' => false Allow {HOST.HOST} and {HOST.HOST<1-9>} macros as host name part in the query.
+ * 'empty_host' => false Allow empty hostname.
+ *
+ * @var array
+ */
+ private $options = [
+ 'usermacros' => false,
+ 'lldmacros' => false,
+ 'calculated' => false,
+ 'host_macro' => false,
+ 'host_macro_n' => false,
+ 'empty_host' => false
+ ];
+
+ /**
+ * @var string
+ */
+ private $host = '';
+
+ /**
+ * @var string
+ */
+ private $item = '';
+
+ /**
+ * @var array
+ */
+ private $filter = [
+ 'match' => '',
+ 'tokens' => []
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+
+ $this->host_name_parser = new CHostNameParser();
+ if ($this->options['host_macro'] || $this->options['host_macro_n']) {
+ $this->host_macro_parser = new CMacroParser([
+ 'macros' => ['{HOST.HOST}'],
+ 'ref_type' => $this->options['host_macro_n']
+ ? CMacroParser::REFERENCE_NUMERIC
+ : CMacroParser::REFERENCE_NONE
+ ]);
+ }
+ $this->item_key_parser = new CItemKey();
+ if ($this->options['calculated']) {
+ $this->filter_parser = new CFilterParser([
+ 'usermacros' => $this->options['usermacros'],
+ 'lldmacros' => $this->options['lldmacros']
+ ]);
+ }
+ }
+
+ /**
+ * Parse a trigger query.
+ *
+ * @param string $source
+ * @param int $pos
+ *
+ * @return int
+ */
+ public function parse($source, $pos = 0): int {
+ $this->match = '';
+ $this->length = 0;
+ $this->host = '';
+ $this->item = '';
+ $this->filter = [
+ 'match' => '',
+ 'tokens' => []
+ ];
+
+ $p = $pos;
+
+ if (!isset($source[$p]) || $source[$p] !== '/') {
+ return CParser::PARSE_FAIL;
+ }
+ $p++;
+
+ if (($this->options['host_macro'] || $this->options['host_macro_n'])
+ && $this->host_macro_parser->parse($source, $p) != self::PARSE_FAIL) {
+ $p += $this->host_macro_parser->getLength();
+ $host = $this->host_macro_parser->getMatch();
+ }
+ // Allow wildcard for calculated item formula.
+ elseif ($this->options['calculated'] && isset($source[$p])
+ && $source[$p] === self::HOST_ITEMKEY_WILDCARD) {
+ $p++;
+ $host = self::HOST_ITEMKEY_WILDCARD;
+ }
+ elseif ($this->host_name_parser->parse($source, $p) != self::PARSE_FAIL) {
+ $p += $this->host_name_parser->getLength();
+ $host = $this->host_name_parser->getMatch();
+ }
+ elseif ($this->options['empty_host']) {
+ $host = '';
+ }
+ else {
+ return CParser::PARSE_FAIL;
+ }
+
+ if (!isset($source[$p]) || $source[$p] !== '/') {
+ return CParser::PARSE_FAIL;
+ }
+ $p++;
+
+ // Allow wildcard for calculated item formula.
+ if ($this->options['calculated'] && isset($source[$p])
+ && $source[$p] === self::HOST_ITEMKEY_WILDCARD) {
+ $p++;
+ $item = self::HOST_ITEMKEY_WILDCARD;
+ }
+ elseif ($this->item_key_parser->parse($source, $p) != self::PARSE_FAIL) {
+ $p += $this->item_key_parser->getLength();
+ $item = $this->item_key_parser->getMatch();
+ }
+ else {
+ return CParser::PARSE_FAIL;
+ }
+
+ if ($this->options['calculated'] && $this->filter_parser->parse($source, $p) != self::PARSE_FAIL) {
+ $this->filter = [
+ 'match' => $this->filter_parser->getMatch(),
+ 'tokens' => $this->filter_parser->getTokens()
+ ];
+ $p += $this->filter_parser->getLength();
+ }
+
+ $this->length = $p - $pos;
+ $this->match = substr($source, $pos, $this->length);
+ $this->host = $host;
+ $this->item = $item;
+
+ return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ }
+
+ /**
+ * Returns the hostname.
+ *
+ * @return string
+ */
+ public function getHost(): string {
+ return $this->host;
+ }
+
+ /**
+ * Returns the item key.
+ *
+ * @return string
+ */
+ public function getItem(): string {
+ return $this->item;
+ }
+
+ /**
+ * Returns the filter.
+ *
+ * @return array
+ */
+ public function getFilter(): array {
+ return $this->filter;
+ }
+}
diff --git a/ui/include/classes/parsers/CRelativeTimeParser.php b/ui/include/classes/parsers/CRelativeTimeParser.php
index 24e2dbb743c..a3624cbc221 100644
--- a/ui/include/classes/parsers/CRelativeTimeParser.php
+++ b/ui/include/classes/parsers/CRelativeTimeParser.php
@@ -32,6 +32,39 @@ class CRelativeTimeParser extends CParser {
*/
private $tokens;
+ private $user_macro_parser;
+ private $lld_macro_parser;
+ private $lld_macro_function_parser;
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'usermacros' => false Enable user macros usage in the periods.
+ * 'lldmacros' => false Enable low-level discovery macros usage in the periods.
+ *
+ * @var array
+ */
+ public $options = [
+ 'usermacros' => false,
+ 'lldmacros' => false
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+
+ if ($this->options['usermacros']) {
+ $this->user_macro_parser = new CUserMacroParser();
+ }
+ if ($this->options['lldmacros']) {
+ $this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
+ }
+ }
+
/**
* Parse the given period.
*
@@ -45,7 +78,7 @@ class CRelativeTimeParser extends CParser {
$p = $pos;
- if (!$this->parseRelativeTime($source, $p)) {
+ if (!$this->parseRelativeTime($source, $p) && !$this->parseMacros($source, $p)) {
return self::PARSE_FAIL;
}
@@ -85,18 +118,27 @@ class CRelativeTimeParser extends CParser {
* @return bool
*/
private function parsePrecision($source, &$pos) {
- $pattern = '(\/[yMwdhm])';
+ $p = $pos;
- if (!preg_match('/^'.$pattern.'/', substr($source, $pos), $matches)) {
+ if (!isset($source[$p]) || $source[$p] !== '/') {
return false;
}
- $this->tokens[] = [
- 'type' => self::ZBX_TOKEN_PRECISION,
- 'suffix' => substr($matches[0], 1)
- ];
+ $p++;
+
+ if (preg_match('/^[yMwdhm]/', substr($source, $p), $matches)) {
+ $this->tokens[] = [
+ 'type' => self::ZBX_TOKEN_PRECISION,
+ 'suffix' => $matches[0]
+ ];
- $pos += strlen($matches[0]);
+ $p++;
+ }
+ elseif (!$this->parseMacros($source, $p)) {
+ return false;
+ }
+
+ $pos = $p;
return true;
}
@@ -110,25 +152,59 @@ class CRelativeTimeParser extends CParser {
* @return bool
*/
private function parseOffset($source, &$pos) {
- $pattern = '(?P<offset_sign>[+-])(?P<offset_value>[0-9]+)(?P<offset_suffix>[yMwdhms])?';
+ $p = $pos;
- if (!preg_match('/^'.$pattern.'/', substr($source, $pos), $matches)) {
+ if (!preg_match('/^[+-]/', substr($source, $p), $sign_matches)) {
return false;
}
- $this->tokens[] = [
- 'type' => self::ZBX_TOKEN_OFFSET,
- 'sign' => $matches['offset_sign'],
- 'value' => $matches['offset_value'],
- 'suffix' => array_key_exists('offset_suffix', $matches) ? $matches['offset_suffix'] : 's'
- ];
+ $p++;
- $pos += strlen($matches[0]);
+ if (preg_match('/^(?P<offset_value>[0-9]+)(?P<offset_suffix>[yMwdhms])?/', substr($source, $p), $matches)) {
+ $this->tokens[] = [
+ 'type' => self::ZBX_TOKEN_OFFSET,
+ 'sign' => $sign_matches[0],
+ 'value' => $matches['offset_value'],
+ 'suffix' => array_key_exists('offset_suffix', $matches) ? $matches['offset_suffix'] : 's'
+ ];
+
+ $p += strlen($matches[0]);
+ }
+ elseif (!$this->parseMacros($source, $p)) {
+ return false;
+ }
+
+ $pos = $p;
return true;
}
/**
+ * Parse macros.
+ *
+ * @param string $source
+ * @param int $pos
+ *
+ * @return bool
+ */
+ private function parseMacros($source, &$pos) {
+ if ($this->options['usermacros'] && $this->user_macro_parser->parse($source, $pos) !== CParser::PARSE_FAIL) {
+ $pos += $this->user_macro_parser->length;
+ }
+ elseif ($this->options['lldmacros'] && $this->lld_macro_parser->parse($source, $pos) !== CParser::PARSE_FAIL) {
+ $pos += $this->lld_macro_parser->length;
+ }
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) !== CParser::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->length;
+ }
+ else {
+ return false;
+ }
+
+ return true;
+ }
+ /**
* Returns an array of tokens.
*
* @return array
diff --git a/ui/include/classes/parsers/results/CTriggerExprParserResult.php b/ui/include/classes/parsers/results/C10TriggerExprParserResult.php
index 8ee558d1e50..ee43616b3be 100644
--- a/ui/include/classes/parsers/results/CTriggerExprParserResult.php
+++ b/ui/include/classes/parsers/results/C10TriggerExprParserResult.php
@@ -18,10 +18,11 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
/**
* Class for storing the result returned by the trigger expression parser.
*/
-class CTriggerExprParserResult extends CParserResult {
+class C10TriggerExprParserResult extends CParserResult {
const TOKEN_TYPE_OPEN_BRACE = 0;
const TOKEN_TYPE_CLOSE_BRACE = 1;
@@ -62,7 +63,7 @@ class CTriggerExprParserResult extends CParserResult {
/**
* Return the expression tokens.
*
- * @see CTriggerExprParserResult::$token for the structure of a token array
+ * @see C10TriggerExprParserResult::$token for the structure of a token array
*
* @return array
*/
diff --git a/ui/include/classes/parsers/results/CExpressionParserResult.php b/ui/include/classes/parsers/results/CExpressionParserResult.php
new file mode 100644
index 00000000000..b2c0abd8962
--- /dev/null
+++ b/ui/include/classes/parsers/results/CExpressionParserResult.php
@@ -0,0 +1,122 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for storing the result returned by the trigger expression parser.
+ */
+class CExpressionParserResult extends CParserResult {
+
+ const TOKEN_TYPE_OPEN_BRACE = 0;
+ const TOKEN_TYPE_CLOSE_BRACE = 1;
+ const TOKEN_TYPE_OPERATOR = 2;
+ const TOKEN_TYPE_NUMBER = 3;
+ const TOKEN_TYPE_MACRO = 4;
+ const TOKEN_TYPE_USER_MACRO = 5;
+ const TOKEN_TYPE_LLD_MACRO = 6;
+ const TOKEN_TYPE_STRING = 7;
+ const TOKEN_TYPE_FUNCTIONID_MACRO = 8;
+ const TOKEN_TYPE_HIST_FUNCTION = 9;
+ const TOKEN_TYPE_MATH_FUNCTION = 10;
+ const TOKEN_TYPE_EXPRESSION = 11;
+
+ /**
+ * Array of tokens.
+ *
+ * @var array
+ */
+ protected $tokens = [];
+
+ /**
+ * Return the expression tokens.
+ *
+ * @see CExpressionParserResult::$token for the structure of a token array
+ *
+ * @return array
+ */
+ public function getTokens() {
+ return $this->tokens;
+ }
+
+ /**
+ * Add a token to the result.
+ *
+ * @param CParserResult $token
+ */
+ public function addTokens(array $tokens): void {
+ $this->tokens = array_merge($this->tokens, $tokens);
+ }
+
+ /**
+ * Auxiliary method for getTokensOfTypes().
+ *
+ * @param array $tokens
+ * @param array $types
+ *
+ * @return array
+ */
+ private static function _getTokensOfTypes(array $tokens, array $types) {
+ $result = [];
+
+ foreach ($tokens as $token) {
+ if (in_array($token['type'], $types)) {
+ $result[] = $token;
+ }
+
+ if ($token['type'] == CExpressionParserResult::TOKEN_TYPE_EXPRESSION) {
+ $result = array_merge($result, self::_getTokensOfTypes($token['data']['tokens'], $types));
+ }
+ elseif ($token['type'] == CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION) {
+ foreach ($token['data']['parameters'] as $parameter) {
+ $result = array_merge($result, self::_getTokensOfTypes($parameter['data']['tokens'], $types));
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns all tokens include nested of the given types.
+ *
+ * @param array $types
+ *
+ * @return array
+ */
+ public function getTokensOfTypes(array $types): array {
+ return self::_getTokensOfTypes($this->tokens, $types);
+ }
+
+ /**
+ * Return list hosts found in parsed trigger expression.
+ *
+ * @return array
+ */
+ public function getHosts(): array {
+ $hist_functions = $this->getTokensOfTypes([CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]);
+ $hosts = [];
+
+ foreach ($hist_functions as $hist_function) {
+ $hosts[$hist_function['data']['parameters'][0]['data']['host']] = true;
+ }
+
+ return array_keys($hosts);
+ }
+}
diff --git a/ui/include/classes/triggers/CTextTriggerConstructor.php b/ui/include/classes/triggers/CTextTriggerConstructor.php
index ad72b5e12b8..68caa078b83 100644
--- a/ui/include/classes/triggers/CTextTriggerConstructor.php
+++ b/ui/include/classes/triggers/CTextTriggerConstructor.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 1);
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -18,6 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
/**
* A class for implementing conversions used by the trigger wizard.
*/
@@ -29,15 +30,15 @@ class CTextTriggerConstructor {
/**
* Parser used for parsing trigger expressions.
*
- * @var CTriggerExpression
+ * @var CExpressionParser
*/
- protected $triggerExpression;
+ protected $expression_parser;
/**
- * @param CTriggerExpression $triggerExpression trigger expression parser
+ * @param CExpressionParser $expression_parser
*/
- public function __construct(CTriggerExpression $triggerExpression) {
- $this->triggerExpression = $triggerExpression;
+ public function __construct(CExpressionParser $expression_parser) {
+ $this->expression_parser = $expression_parser;
}
/**
@@ -47,7 +48,7 @@ class CTextTriggerConstructor {
* Feel free to rewrite and correct it if necessary.
*
* @param string $host host name
- * @param string $itemKey item key
+ * @param string $item_key item key
* @param array $expressions array of expression parts
* @param string $expressions[]['value'] expression string
* @param int $expressions[]['type'] whether the string should match the expression; supported values:
@@ -55,12 +56,13 @@ class CTextTriggerConstructor {
*
* @return bool|string
*/
- public function getExpressionFromParts($host, $itemKey, array $expressions) {
+ public function getExpressionFromParts(string $host, string $item_key, array $expressions) {
$result = '';
- $prefix = $host.':'.$itemKey.'.';
+ $query = '/'.$host.'/'.$item_key;
if (empty($expressions)) {
error(_('Expression cannot be empty'));
+
return false;
}
@@ -74,7 +76,7 @@ class CTextTriggerConstructor {
foreach ($expressions as $expression) {
if ($expression['type'] == self::EXPRESSION_TYPE_MATCH) {
if (!empty($result)) {
- $result.=' or ';
+ $result .= ' or ';
}
if ($cexpor == 0) {
$startpos = mb_strlen($result);
@@ -91,7 +93,7 @@ class CTextTriggerConstructor {
$cexpor = 0;
$eq_global = '=0';
if (!empty($result)) {
- $result.=' and ';
+ $result .= ' and ';
}
}
@@ -101,7 +103,7 @@ class CTextTriggerConstructor {
$expr = preg_replace('/\s+(and|or)\s+/U', ' $1 ', $expr);
$expr_array = [];
- $sub_expr_count=0;
+ $sub_expr_count = 0;
$sub_expr = '';
$multi = preg_match('/.+(and|or).+/', $expr);
@@ -111,12 +113,14 @@ class CTextTriggerConstructor {
$arr[6] = strtolower($arr[6]);
if (!isset($functions[$arr[6]])) {
error(_('Incorrect function is used').'. ['.$expression['value'].']');
+
return false;
}
$expr_array[$sub_expr_count]['eq'] = trim($arr[2]);
$expr_array[$sub_expr_count]['not'] = trim($arr[3]);
$expr_array[$sub_expr_count]['minus'] = trim($arr[4]);
- $expr_array[$sub_expr_count]['regexp'] = $arr[6].$arr[7];
+ $expr_array[$sub_expr_count]['func'] = $arr[6];
+ $expr_array[$sub_expr_count]['pattern'] = $arr[8];
$sub_expr_count++;
$expr = $arr[1];
@@ -124,6 +128,7 @@ class CTextTriggerConstructor {
if (empty($expr_array)) {
error(_('Incorrect trigger expression').'. ['.$expression['value'].']');
+
return false;
}
@@ -137,11 +142,12 @@ class CTextTriggerConstructor {
foreach ($expr_array as $id => $expr) {
$eq = ($expr['eq'] === '') ? '' : ' '.$expr['eq'].' ';
$not = ($expr['not'] === '') ? '' : $expr['not'].' ';
+ $function = 'find('.$query.',,"'.$expr['func'].'","'.$expr['pattern'].'")';
if ($multi > 0) {
- $sub_expr = $eq.'('.$not.$expr['minus'].'{'.$prefix.$expr['regexp'].'})'.$sub_eq.$sub_expr;
+ $sub_expr = $eq.'('.$not.$expr['minus'].$function.')'.$sub_eq.$sub_expr;
}
else {
- $sub_expr = $eq.$expr['eq'].$not.$expr['minus'].'{'.$prefix.$expr['regexp'].'}'.$sub_eq.$sub_expr;
+ $sub_expr = $eq.$expr['eq'].$not.$expr['minus'].$function.')'.$sub_eq.$sub_expr;
}
}
@@ -166,7 +172,7 @@ class CTextTriggerConstructor {
* Break a trigger expression generated by the constructor.
*
* To be successfully parsed, each item function macro must be wrapped in additional parentheses, for example,
- * (({item.item.regex(param)})=0)
+ * ((find(item.item,,regex,param))=0)
*
* Most of this function was left unchanged to preserve the current behavior of the constructor.
* Feel free to rewrite and correct it if necessary.
@@ -181,27 +187,33 @@ class CTextTriggerConstructor {
$expression = preg_replace('/\(\(\((.+?)\)\) and/i', '(($1) and', $expression);
$expression = preg_replace('/\(\(\((.+?)\)\)$/i', '(($1)', $expression);
- $parseResult = $this->triggerExpression->parse($expression);
+ $this->expression_parser->parse($expression);
$expressions = [];
- $splitTokens = $this->splitTokensByFirstLevel($parseResult->getTokens());
- foreach($splitTokens as $key => $tokens) {
+ $splitTokens = $this->splitTokensByFirstLevel($this->expression_parser->getResult()->getTokens());
+ foreach ($splitTokens as $key => $tokens) {
$expr = [];
// replace whole function macros with their functions
foreach ($tokens as $token) {
- $value = $token['value'];
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO:
- $value = $token['data']['function'];
-
+ case CExpressionParserResult::TOKEN_TYPE_OPERATOR:
+ $value = ($token['match'] === 'and' || $token['match'] === 'or' || $token['match'] === 'not')
+ ? ' '.$token['match'].' '
+ : $token['match'];
break;
- case CTriggerExprParserResult::TOKEN_TYPE_OPERATOR:
- if ($token['value'] === 'and' || $token['value'] === 'or' || $token['value'] === 'not') {
- $value = ' '.$token['value'].' ';
+
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ if ($token['data']['function'] === 'find' && count($token['data']['parameters']) == 4) {
+ $function = CExpressionParser::unquoteString($token['data']['parameters'][2]['match']);
+ $pattern = CExpressionParser::unquoteString($token['data']['parameters'][3]['match']);
+ $value = $function.'('.$pattern.')';
+ break;
}
+ // break; is not missing here
- break;
+ default:
+ $value = $token['match'];
}
$expr[] = $value;
@@ -232,7 +244,7 @@ class CTextTriggerConstructor {
*
* The tokens are split at the first occurrence of the "and" or "or" operators with respect to parentheses.
*
- * @param array $tokens an array of tokens from the CTriggerExprParserResult
+ * @param array $tokens an array of tokens from the CExpressionParserResult
*
* @return array an array of token arrays grouped by expression
*/
@@ -243,10 +255,10 @@ class CTextTriggerConstructor {
$level = 0;
foreach ($tokens as $token) {
switch ($token['type']) {
- case CTriggerExprParserResult::TOKEN_TYPE_OPERATOR:
+ case CExpressionParserResult::TOKEN_TYPE_OPERATOR:
// look for an "or" or "and" operator on the top parentheses level
// if such an expression is found, save all of the tokens before it as a separate expression
- if ($level == 0 && ($token['value'] === 'or' || $token['value'] === 'and')) {
+ if ($level == 0 && ($token['match'] === 'or' || $token['match'] === 'and')) {
$expressions[] = $currentExpression;
$currentExpression = [];
@@ -255,11 +267,11 @@ class CTextTriggerConstructor {
}
break;
- case CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE:
+ case CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE:
$level++;
break;
- case CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE:
+ case CExpressionParserResult::TOKEN_TYPE_CLOSE_BRACE:
$level--;
break;
diff --git a/ui/include/classes/validators/CApiInputValidator.php b/ui/include/classes/validators/CApiInputValidator.php
index 99cac9956ce..5c5ce5c31c5 100644
--- a/ui/include/classes/validators/CApiInputValidator.php
+++ b/ui/include/classes/validators/CApiInputValidator.php
@@ -317,6 +317,7 @@ class CApiInputValidator {
*
* @param array $rule
* @param int $rule['flags'] (optional) API_ALLOW_LLD_MACRO
+ * @param int $rule['length'] (optional)
* @param mixed $data
* @param string $path
* @param string $error
@@ -330,9 +331,27 @@ class CApiInputValidator {
return false;
}
- $expression_data = new CTriggerExpression(['calculated' => true, 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO)]);
- if (!$expression_data->parse($data)) {
- $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_data->error);
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+ return false;
+ }
+
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO),
+ 'calculated' => true,
+ 'host_macro' => true,
+ 'empty_host' => true
+ ]);
+
+ if ($expression_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_parser->getError());
+ return false;
+ }
+
+ $expression_validator = new CExpressionValidator(['calculated' => true]);
+
+ if (!$expression_validator->validate($expression_parser->getResult()->getTokens())) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_validator->getError());
return false;
}
@@ -2035,20 +2054,19 @@ class CApiInputValidator {
return false;
}
- $expression_data = new CTriggerExpression([
- 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO),
- 'lowercase_errors' => true
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO)
]);
- if (!$expression_data->parse($data)) {
- $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_data->error);
+ if ($expression_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_parser->getError());
return false;
}
- if (!$expression_data->expressions) {
- $error = _s('Invalid parameter "%1$s": %2$s.', $path,
- _('trigger expression must contain at least one host:key reference')
- );
+ $expression_validator = new CExpressionValidator();
+
+ if (!$expression_validator->validate($expression_parser->getResult()->getTokens())) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_validator->getError());
return false;
}
diff --git a/ui/include/classes/validators/CExpressionValidator.php b/ui/include/classes/validators/CExpressionValidator.php
new file mode 100644
index 00000000000..c0adccdd33e
--- /dev/null
+++ b/ui/include/classes/validators/CExpressionValidator.php
@@ -0,0 +1,221 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for validating trigger expressions and calculated item formulas.
+ */
+class CExpressionValidator extends CValidator {
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'calculated' => false Validate expression as part of calculated item formula.
+ *
+ * @var array
+ */
+ private $options = [
+ 'calculated' => false
+ ];
+
+ /**
+ * Provider of information on math functions.
+ *
+ * @var CMathFunctionData
+ */
+ private $math_function_data;
+
+ /**
+ * Known math functions along with number of required parameters.
+ *
+ * @var array
+ */
+ private $math_function_parameters = [];
+
+ /**
+ * Provider of information on history functions.
+ *
+ * @var CHistFunctionData
+ */
+ private $hist_function_data;
+
+ /**
+ * Known history functions along with definition of parameters.
+ *
+ * @var array
+ */
+ private $hist_function_parameters = [];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+
+ $this->math_function_data = new CMathFunctionData();
+ $this->math_function_parameters = $this->math_function_data->getParameters();
+
+ $this->hist_function_data = new CHistFunctionData(['calculated' => $this->options['calculated']]);
+ $this->hist_function_parameters = $this->hist_function_data->getParameters();
+ }
+
+ /**
+ * Validate expression.
+ *
+ * @param array $tokens A hierarchy of tokens of parsed expression.
+ *
+ * @return bool
+ */
+ public function validate($tokens) {
+ if (!$this->validateRecursively($tokens, null)) {
+ return false;
+ }
+
+ if (!$this->options['calculated']) {
+ if (!self::hasHistoryFunctions($tokens)) {
+ $this->setError(_('trigger expression must contain at least one /host/key reference'));
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate expression (recursive helper).
+ *
+ * @param array $tokens A hierarchy of tokens.
+ * @param array|null $parent_token Parent token containing the hierarchy of tokens.
+ *
+ * @return bool
+ */
+ private function validateRecursively(array $tokens, ?array $parent_token): bool {
+ foreach ($tokens as $token) {
+ switch ($token['type']) {
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ if (!$this->math_function_data->isKnownFunction($token['data']['function'])
+ && $this->hist_function_data->isKnownFunction($token['data']['function'])) {
+ $this->setError(_s('incorrect usage of function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ $math_function_validator = new CMathFunctionValidator([
+ 'parameters' => $this->math_function_parameters
+ ]);
+
+ if (!$math_function_validator->validate($token)) {
+ $this->setError($math_function_validator->getError());
+
+ return false;
+ }
+
+ foreach ($token['data']['parameters'] as $parameter) {
+ if (!$this->validateRecursively($parameter['data']['tokens'], $token)) {
+ return false;
+ }
+ }
+
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ if (!$this->hist_function_data->isKnownFunction($token['data']['function'])
+ && $this->math_function_data->isKnownFunction($token['data']['function'])) {
+ $this->setError(_s('incorrect usage of function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ $options = [
+ 'parameters' => $this->hist_function_parameters,
+ 'calculated' => $this->options['calculated']
+ ];
+
+ if ($this->options['calculated']) {
+ $options['aggregating'] = CHistFunctionData::isAggregating($token['data']['function']);
+ }
+
+ $hist_function_validator = new CHistFunctionValidator($options);
+
+ if (!$hist_function_validator->validate($token)) {
+ $this->setError($hist_function_validator->getError());
+
+ return false;
+ }
+
+ if ($options['calculated'] && $options['aggregating']) {
+ if ($parent_token === null
+ || $parent_token['type'] != CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION
+ || !$this->math_function_data->isAggregating($parent_token['data']['function'])
+ || count($parent_token['data']['parameters']) != 1) {
+ $this->setError(_s('incorrect usage of function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+ }
+
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_EXPRESSION:
+ if (!$this->validateRecursively($parameter['data']['tokens'], $parent_token)) {
+ return false;
+ }
+
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if history function tokens are contained within the hierarchy of given tokens.
+ *
+ * @param array $tokens
+ *
+ * @static
+ *
+ * @return bool
+ */
+ private static function hasHistoryFunctions(array $tokens): bool {
+ foreach ($tokens as $token) {
+ switch ($token['type']) {
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ foreach ($token['data']['parameters'] as $parameter) {
+ if (self::hasHistoryFunctions($parameter['data']['tokens'])) {
+ return true;
+ }
+ }
+
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ return true;
+
+ case CExpressionParserResult::TOKEN_TYPE_EXPRESSION:
+ return self::hasHistoryFunctions($parameter['data']['tokens']);
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php
deleted file mode 100644
index e701a2ec60b..00000000000
--- a/ui/include/classes/validators/CFunctionValidator.php
+++ /dev/null
@@ -1,693 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2021 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CFunctionValidator extends CValidator {
-
- /**
- * The array containing valid functions and parameters to them.
- *
- * Structure: array(
- * '<function>' => array(
- * 'args' => array(
- * array('type' => '<parameter_type>'[, 'mandat' => bool]),
- * ...
- * ),
- * 'value_types' => array(<value_type>, <value_type>, ...)
- * )
- * )
- *
- * <parameter_type> can be 'fit', 'mode', 'num_suffix', 'num_unsigned', 'operation', 'percent', 'sec_neg',
- * 'sec_num', 'sec_num_zero', 'sec_zero'
- * <value_type> can be one of ITEM_VALUE_TYPE_*
- *
- * @var array
- */
- private $allowed;
-
- /**
- * If set to true, LLD macros can be uses inside functions and are properly validated using LLD macro parser.
- *
- * @var bool
- */
- private $lldmacros = true;
-
- public function __construct(array $options = []) {
- /*
- * CValidator is an abstract class, so no specific functionality should be bound to it. Thus putting
- * an option "lldmacros" (or class variable $lldmacros) in it, is not preferred. Without it, class
- * initialization would fail due to __set(). So instead we create a local variable in this extended class
- * and remove the option "lldmacros" before calling the parent constructor.
- */
- if (array_key_exists('lldmacros', $options)) {
- $this->lldmacros = $options['lldmacros'];
- unset($options['lldmacros']);
- }
- parent::__construct($options);
-
- $valueTypesAll = [
- ITEM_VALUE_TYPE_FLOAT => true,
- ITEM_VALUE_TYPE_UINT64 => true,
- ITEM_VALUE_TYPE_STR => true,
- ITEM_VALUE_TYPE_TEXT => true,
- ITEM_VALUE_TYPE_LOG => true
- ];
- $valueTypesNum = [
- ITEM_VALUE_TYPE_FLOAT => true,
- ITEM_VALUE_TYPE_UINT64 => true
- ];
- $valueTypesChar = [
- ITEM_VALUE_TYPE_STR => true,
- ITEM_VALUE_TYPE_TEXT => true,
- ITEM_VALUE_TYPE_LOG => true
- ];
- $valueTypesLog = [
- ITEM_VALUE_TYPE_LOG => true
- ];
- $valueTypesInt = [
- ITEM_VALUE_TYPE_UINT64 => true
- ];
-
- $argsIgnored = [['type' => 'str']];
-
- $this->allowed = [
- 'abschange' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'avg' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'band' => [
- 'args' => [
- ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true],
- ['type' => 'num_unsigned', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesInt
- ],
- 'change' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'count' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'str'],
- ['type' => 'operation'],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesAll
- ],
- 'date' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'dayofmonth' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'dayofweek' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'delta' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'diff' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'forecast' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true],
- ['type' => 'sec_neg', 'mandat' => true],
- ['type' => 'fit', 'can_be_empty' => true],
- ['type' => 'mode', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'fuzzytime' => [
- 'args' => [
- ['type' => 'sec_zero', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'iregexp' => [
- 'args' => [
- ['type' => 'str', 'mandat' => true],
- ['type' => 'sec_num', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesChar
- ],
- 'last' => [
- 'args' => [
- ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesAll
- ],
- 'logeventid' => [
- 'args' => [
- ['type' => 'str', 'mandat' => true]
- ],
- 'value_types' => $valueTypesLog
- ],
- 'logseverity' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesLog
- ],
- 'logsource' => [
- 'args' => [
- ['type' => 'str', 'mandat' => true]
- ],
- 'value_types' => $valueTypesLog
- ],
- 'max' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'min' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'nodata'=> [
- 'args' => [
- ['type' => 'sec', 'mandat' => true],
- ['type' => 'nodata_mode', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesAll
- ],
- 'now' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'percentile' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true],
- ['type' => 'percent', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'prev' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'regexp' => [
- 'args' => [
- ['type' => 'str', 'mandat' => true],
- ['type' => 'sec_num', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesChar
- ],
- 'str' => [
- 'args' => [
- ['type' => 'str', 'mandat' => true],
- ['type' => 'sec_num', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesChar
- ],
- 'strlen' => [
- 'args' => [
- ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesChar
- ],
- 'sum' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'time' => [
- 'args' => $argsIgnored,
- 'value_types' => $valueTypesAll
- ],
- 'timeleft' => [
- 'args' => [
- ['type' => 'sec_num', 'mandat' => true],
- ['type' => 'sec_zero', 'can_be_empty' => true],
- ['type' => 'num_suffix', 'mandat' => true],
- ['type' => 'fit', 'can_be_empty' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'trendavg' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'trendcount' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesAll
- ],
- 'trenddelta' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'trendmax' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'trendmin' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ],
- 'trendsum' => [
- 'args' => [
- ['type' => 'period', 'mandat' => true],
- ['type' => 'period_shift', 'mandat' => true]
- ],
- 'value_types' => $valueTypesNum
- ]
- ];
- }
-
- /**
- * Validate trigger function like last(0), time(), etc.
- * Examples:
- * array(
- * 'function' => last("#15"),
- * 'functionName' => 'last',
- * 'functionParamList' => array(0 => '#15'),
- * 'valueType' => 3
- * )
- *
- * @param string $value['function']
- * @param string $value['functionName']
- * @param array $value['functionParamList']
- * @param int $value['valueType']
- *
- * @return bool
- */
- public function validate($value) {
- $this->setError('');
-
- if (!isset($this->allowed[$value['functionName']])) {
- $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '.
- _('Unknown function.'));
- return false;
- }
-
- if (!isset($this->allowed[$value['functionName']]['value_types'][$value['valueType']])) {
- $this->setError(_s('Incorrect item value type "%1$s" provided for trigger function "%2$s".',
- itemValueTypeString($value['valueType']), $value['function']));
- return false;
- }
-
- if (count($this->allowed[$value['functionName']]['args']) < count($value['functionParamList'])) {
- $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '.
- _('Invalid number of parameters.'));
- return false;
- }
-
- $paramLabels = [
- _('Invalid first parameter.'),
- _('Invalid second parameter.'),
- _('Invalid third parameter.'),
- _('Invalid fourth parameter.'),
- _('Invalid fifth parameter.')
- ];
-
- $user_macro_parser = new CUserMacroParser();
- if ($this->lldmacros) {
- $lld_macro_parser = new CLLDMacroParser();
- $lld_macro_function_parser = new CLLDMacroFunctionParser();
- }
-
- $func_args = [];
-
- foreach ($this->allowed[$value['functionName']]['args'] as $aNum => $arg) {
- // mandatory check
- if (isset($arg['mandat']) && $arg['mandat'] && !isset($value['functionParamList'][$aNum])) {
- $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '.
- _('Mandatory parameter is missing.'));
- return false;
- }
-
- if (!isset($value['functionParamList'][$aNum])) {
- continue;
- }
-
- if (isset($arg['can_be_empty']) && $value['functionParamList'][$aNum] == '') {
- continue;
- }
-
- // user macro
- if ($user_macro_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS) {
- continue;
- }
-
- if ($this->lldmacros
- && ($lld_macro_function_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS
- || $lld_macro_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS)) {
- continue;
- }
-
- if (!$this->validateParameter($value['functionParamList'][$aNum], $arg['type'])) {
- $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.',
- $value['function']).' '.$paramLabels[$aNum]);
- return false;
- }
-
- $func_args[$arg['type']] = $value['functionParamList'][$aNum];
- }
-
- if (array_key_exists('period', $func_args) && array_key_exists('period_shift', $func_args)
- && !$this->validateTrendPeriods($func_args['period'], $func_args['period_shift'])) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Validate trend* function used period and period_shift arguments.
- *
- * @param string $period_value Value of period, first argument for trend* function.
- * @param string $period_shift_value Value of period shift, second argument for trend* function.
- *
- * @return bool
- */
- private function validateTrendPeriods($period_value, $period_shift_value): bool {
- $precisions = 'hdwMy';
- $period = strpos($precisions, substr($period_value, -1));
-
- if ($period !== false) {
- $relative_time_parser = new CRelativeTimeParser();
- $relative_time_parser->parse($period_shift_value);
-
- foreach ($relative_time_parser->getTokens() as $token) {
- if ($token['type'] !== CRelativeTimeParser::ZBX_TOKEN_PRECISION) {
- continue;
- }
-
- if (strpos($precisions, $token['suffix']) < $period) {
- $this->setError(_('Time units in period shift must be greater or equal to period time unit.'));
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Validate trigger function parameter.
- *
- * @param string $param
- * @param string $type type of $param ('fit', 'mode', 'num_suffix', 'num_unsigned', 'operation', 'percent',
- * 'period', 'period_shift', 'sec_neg', 'sec_num', 'sec_num_zero', 'sec_zero')
- *
- * @return bool
- */
- private function validateParameter($param, $type) {
- switch ($type) {
- case 'sec':
- return $this->validateSec($param);
-
- case 'sec_zero':
- return $this->validateSecZero($param);
-
- case 'sec_neg':
- return $this->validateSecNeg($param);
-
- case 'sec_num':
- return $this->validateSecNum($param);
-
- case 'sec_num_zero':
- return $this->validateSecNumZero($param);
-
- case 'num_unsigned':
- return CNewValidator::is_uint64($param);
-
- case 'num_suffix':
- return $this->validateNumSuffix($param);
-
- case 'nodata_mode':
- return ($param === 'strict');
-
- case 'fit':
- return $this->validateFit($param);
-
- case 'mode':
- return $this->validateMode($param);
-
- case 'percent':
- return $this->validatePercent($param);
-
- case 'operation':
- return $this->validateOperation($param);
-
- case 'period':
- return $this->validatePeriod($param);
-
- case 'period_shift':
- return $this->validatePeriodShift($param);
- }
-
- return true;
- }
-
- /**
- * Validate trigger function parameter seconds value.
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSecValue($param) {
- return preg_match('/^\d+['.ZBX_TIME_SUFFIXES.']{0,1}$/', $param);
- }
-
- /**
- * Validate trigger function parameter which can contain only seconds.
- * Examples: 1, 5w
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSec($param) {
- return ($this->validateSecValue($param) && $param > 0);
- }
-
- /**
- * Validate trigger function parameter which can contain only seconds or zero.
- * Examples: 0, 1, 5w
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSecZero($param) {
- return $this->validateSecValue($param);
- }
-
- /**
- * Validate trigger function parameter which can contain negative seconds.
- * Examples: 0, 1, 5w, -3h
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSecNeg($param) {
- return preg_match('/^[-]?\d+['.ZBX_TIME_SUFFIXES.']{0,1}$/', $param);
- }
-
- /**
- * Validate trigger function parameter which can contain seconds greater zero or count.
- * Examples: 1, 5w, #1
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSecNum($param) {
- if (preg_match('/^#\d+$/', $param)) {
- return (substr($param, 1) > 0);
- }
-
- return ($this->validateSecValue($param) && $param > 0);
- }
-
- /**
- * Validate trigger function parameter which can contain seconds or count.
- * Examples: 0, 1, 5w, #1
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateSecNumZero($param) {
- if (preg_match('/^#\d+$/', $param)) {
- return (substr($param, 1) > 0);
- }
-
- return $this->validateSecValue($param);
- }
-
- /**
- * Validate trigger function parameter which can contain suffixed decimal number.
- * Examples: 0, 1, 5w, -3h, 10.2G
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateNumSuffix($param) {
- return ((new CNumberParser(['with_suffix' => true]))->parse($param) == CParser::PARSE_SUCCESS);
- }
-
- /**
- * Validate trigger function parameter which can contain fit function (linear, polynomialN with 1 <= N <= 6,
- * exponential, logarithmic, power) or an empty value.
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateFit($param) {
- return preg_match('/^(linear|polynomial[1-6]|exponential|logarithmic|power|)$/', $param);
- }
-
- /**
- * Validate trigger function parameter which can contain forecast mode (value, max, min, delta, avg) or
- * an empty value.
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateMode($param) {
- return preg_match('/^(value|max|min|delta|avg|)$/', $param);
- }
-
- /**
- * Validate trigger function parameter which can contain a percentage.
- * Examples: 0, 1, 1.2, 1.2345, 1., .1, 100
- *
- * @param string $param
- *
- * @return bool
- */
- private function validatePercent($param) {
- return (preg_match('/^\d*(\.\d{0,4})?$/', $param) && $param !== '.' && $param <= 100);
- }
-
- /**
- * Validate trigger function parameter which can contain operation (band, eq, ge, gt, le, like, lt, ne,
- * regexp, iregexp) or an empty value.
- *
- * @param string $param
- *
- * @return bool
- */
- private function validateOperation($param) {
- return preg_match('/^(eq|ne|gt|ge|lt|le|like|band|regexp|iregexp|)$/', $param);
- }
-
- /**
- * Validate trigger function parameter which can contain time unit not less than 1 hour and multiple of an hour.
- * Examples: 3600, 7200s, 2h, 1d
- *
- * @param string $param
- *
- * @return bool
- */
- private function validatePeriod(string $param): bool {
- $simple_interval_parser = new CSimpleIntervalParser(['with_year' => true]);
- $value = (string) $param;
-
- if ($simple_interval_parser->parse($value) == CParser::PARSE_SUCCESS) {
- $value = timeUnitToSeconds($value, true);
-
- if ($value >= SEC_PER_HOUR && $value % SEC_PER_HOUR === 0) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Validate trigger function parameter which can contain time range value with precision and multiple of an hour.
- * Examples: now/h, now/w, now/M, now/y
- *
- * @param string $param
- *
- * @return bool
- */
- private function validatePeriodShift(string $param): bool {
- $relative_time_parser = new CRelativeTimeParser();
-
- if ($relative_time_parser->parse((string) $param) === CParser::PARSE_SUCCESS) {
- $tokens = $relative_time_parser->getTokens();
-
- foreach ($tokens as $token) {
- if ($token['type'] === CRelativeTimeParser::ZBX_TOKEN_PRECISION && $token['suffix'] === 'm') {
- return false;
- }
- }
-
- foreach ($tokens as $token) {
- if ($token['type'] === CRelativeTimeParser::ZBX_TOKEN_PRECISION
- && $relative_time_parser->getDateTime(true)->getTimestamp() % SEC_PER_HOUR === 0) {
- return true;
- }
- }
- }
-
- return false;
- }
-}
diff --git a/ui/include/classes/validators/CHistFunctionValidator.php b/ui/include/classes/validators/CHistFunctionValidator.php
new file mode 100644
index 00000000000..a16d7d7e492
--- /dev/null
+++ b/ui/include/classes/validators/CHistFunctionValidator.php
@@ -0,0 +1,348 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for validating history functions.
+ */
+class CHistFunctionValidator extends CValidator {
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'parameters' => [] Definition of parameters of known history functions.
+ * 'calculated' => false Validate history function as part of calculated item formula.
+ * 'aggregating' => false Validate as aggregating history function.
+ *
+ * @var array
+ */
+ private $options = [
+ 'parameters' => [],
+ 'calculated' => false,
+ 'aggregating' => false
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+ }
+
+ /**
+ * Validate history function.
+ *
+ * @param array $token A token of CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION type.
+ *
+ * @return bool
+ */
+ public function validate($token) {
+ $invalid_param_messages = [
+ _('invalid first parameter in function "%1$s"'),
+ _('invalid second parameter in function "%1$s"'),
+ _('invalid third parameter in function "%1$s"'),
+ _('invalid fourth parameter in function "%1$s"'),
+ _('invalid fifth parameter in function "%1$s"')
+ ];
+
+ if (!array_key_exists($token['data']['function'], $this->options['parameters'])) {
+ $this->setError(_s('unknown function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ $params = $token['data']['parameters'];
+ $params_spec = $this->options['parameters'][$token['data']['function']];
+
+ if (count($params) > count($params_spec)) {
+ $this->setError(_s('invalid number of parameters in function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ foreach ($params_spec as $index => $param_spec) {
+ $required = !array_key_exists('required', $param_spec) || $param_spec['required'];
+
+ if ($index >= count($params)) {
+ if ($required) {
+ $this->setError(
+ _s('mandatory parameter is missing in function "%1$s"', $token['data']['function'])
+ );
+
+ return false;
+ }
+
+ continue;
+ }
+
+ $param = $params[$index];
+
+ if ($param['match'] === '') {
+ if ($required) {
+ $this->setError(_params($invalid_param_messages[$index], [$token['data']['function']]));
+
+ return false;
+ }
+
+ continue;
+ }
+
+ switch ($param['type']) {
+ case CHistFunctionParser::PARAM_TYPE_PERIOD:
+ if (self::hasMacros($param['data']['sec_num']) && $param['data']['time_shift'] === '') {
+ continue 2;
+ }
+ break;
+
+ case CHistFunctionParser::PARAM_TYPE_QUOTED:
+ if (self::hasMacros(CHistFunctionParser::unquoteParam($param['match']))) {
+ continue 2;
+ }
+ break;
+
+ case CHistFunctionParser::PARAM_TYPE_UNQUOTED:
+ if (self::hasMacros($param['match'])) {
+ continue 2;
+ }
+ break;
+ }
+
+ if (array_key_exists('rules', $param_spec)) {
+ $is_valid = self::validateRules($param, $param_spec['rules'], $this->options);
+
+ if (!$is_valid) {
+ $this->setError(_params($invalid_param_messages[$index], [$token['data']['function']]));
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Loose check if string value contains macros.
+ *
+ * @param string $value
+ *
+ * @static
+ *
+ * @return bool
+ */
+ private static function hasMacros(string $value): bool {
+ return (strpos($value, '{') !== false);
+ }
+
+ /**
+ * Validate function parameter token's compliance to the rules.
+ *
+ * @param array $param Function parameter token.
+ * @param array $rules
+ * @param array $options
+ *
+ * @static
+ *
+ * @return bool
+ */
+ private static function validateRules(array $param, array $rules, array $options): bool {
+ $param_match_unquoted = ($param['type'] == CHistFunctionParser::PARAM_TYPE_QUOTED)
+ ? CHistFunctionParser::unquoteParam($param['match'])
+ : $param['match'];
+
+ foreach ($rules as $rule) {
+ switch ($rule['type']) {
+ case 'query':
+ if ($param['type'] != CHistFunctionParser::PARAM_TYPE_QUERY) {
+ return false;
+ }
+
+ if (!self::validateQuery($param['data']['host'], $param['data']['item'], $param['data']['filter'],
+ $options)) {
+ return false;
+ }
+
+ break;
+
+ case 'period':
+ if ($param['type'] != CHistFunctionParser::PARAM_TYPE_PERIOD) {
+ return false;
+ }
+
+ if (!self::validatePeriod($param['data']['sec_num'], $param['data']['time_shift'], $rule['mode'])) {
+ return false;
+ }
+
+ break;
+
+ case 'number':
+ $with_suffix = array_key_exists('with_suffix', $rule) && $rule['with_suffix'];
+
+ $parser = new CNumberParser(['with_minus' => true, 'with_suffix' => $with_suffix]);
+
+ if ($parser->parse($param_match_unquoted) != CParser::PARSE_SUCCESS) {
+ return false;
+ }
+
+ $value = $parser->calcValue();
+
+ if ((array_key_exists('min', $rule) && $value < $rule['min'])
+ || array_key_exists('max', $rule) && $value > $rule['max']) {
+ return false;
+ }
+
+ break;
+
+ case 'regexp':
+ if (preg_match($rule['pattern'], $param_match_unquoted) != 1) {
+ return false;
+ }
+
+ break;
+
+ case 'time':
+ $with_year = array_key_exists('with_year', $rule) && $rule['with_year'];
+ $min = array_key_exists('min', $rule) ? $rule['min'] : ZBX_MIN_INT32;
+ $max = array_key_exists('max', $rule) ? $rule['max'] : ZBX_MAX_INT32;
+
+ $sec = timeUnitToSeconds($param_match_unquoted, $with_year);
+
+ if ($sec === null || $sec < $min || $sec > $max) {
+ return false;
+ }
+
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate function's query parameter.
+ *
+ * @param string $host
+ * @param string $item
+ * @param array $filter Filter token.
+ * @param array $options
+ *
+ * @static
+ *
+ * @return bool
+ */
+ private static function validateQuery(string $host, string $item, array $filter, array $options): bool {
+ if ($options['calculated']) {
+ if ($options['aggregating']) {
+ if ($host === CQueryParser::HOST_ITEMKEY_WILDCARD && $item === CQueryParser::HOST_ITEMKEY_WILDCARD) {
+ return false;
+ }
+ }
+ else {
+ if ($filter['match'] !== '') {
+ return false;
+ }
+
+ if ($host === CQueryParser::HOST_ITEMKEY_WILDCARD || $item === CQueryParser::HOST_ITEMKEY_WILDCARD) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate function's period parameter.
+ *
+ * @param string $sec_num
+ * @param string $time_shift
+ * @param int $mode
+ *
+ * @static
+ *
+ * @return bool
+ */
+ private static function validatePeriod(string $sec_num, string $time_shift, int $mode): bool {
+ switch ($mode) {
+ case CHistFunctionData::PERIOD_MODE_DEFAULT:
+ if ($sec_num === '' || self::hasMacros($sec_num)) {
+ return true;
+ }
+
+ $sec = timeUnitToSeconds($sec_num);
+
+ if ($sec !== null) {
+ return ($sec > 0 && $sec <= ZBX_MAX_INT32);
+ }
+
+ if (preg_match('/^#(?<num>\d+)$/', $sec_num, $matches) == 1) {
+ return ($matches['num'] > 0 && $matches['num'] <= ZBX_MAX_INT32);
+ }
+
+ return false;
+
+ case CHistFunctionData::PERIOD_MODE_SEC:
+ if ($time_shift !== '') {
+ return false;
+ }
+
+ $sec = timeUnitToSeconds($sec_num);
+
+ if ($sec !== null) {
+ return ($sec > 0 && $sec <= ZBX_MAX_INT32);
+ }
+
+ return false;
+
+ case CHistFunctionData::PERIOD_MODE_NUM:
+ if (preg_match('/^#(?<num>\d+)$/', $sec_num, $matches) == 1) {
+ return ($matches['num'] > 0 && $matches['num'] <= ZBX_MAX_INT32);
+ }
+
+ return false;
+
+ case CHistFunctionData::PERIOD_MODE_TREND:
+ if ($time_shift === '') {
+ return false;
+ }
+
+ if (self::hasMacros($sec_num)) {
+ return true;
+ }
+
+ $sec = timeUnitToSeconds($sec_num, true);
+
+ if ($sec !== null) {
+ return ($sec > 0 && $sec <= ZBX_MAX_INT32 && $sec % SEC_PER_HOUR == 0);
+ }
+
+ return false;
+
+ default:
+ return false;
+ }
+
+ return false;
+ }
+}
diff --git a/ui/include/classes/validators/CMathFunctionValidator.php b/ui/include/classes/validators/CMathFunctionValidator.php
new file mode 100644
index 00000000000..d0187358914
--- /dev/null
+++ b/ui/include/classes/validators/CMathFunctionValidator.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for validating math functions.
+ */
+class CMathFunctionValidator extends CValidator {
+
+ /**
+ * An options array.
+ *
+ * Supported options:
+ * 'parameters' => [] Number of required parameters of known math functions.
+ *
+ * @var array
+ */
+ private $options = [
+ 'parameters' => []
+ ];
+
+ /**
+ * @param array $options
+ */
+ public function __construct(array $options = []) {
+ $this->options = $options + $this->options;
+ }
+
+ /**
+ * Validate math function.
+ *
+ * @param array $token A token of CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION type.
+ *
+ * @return bool
+ */
+ public function validate($token) {
+ if (!array_key_exists($token['data']['function'], $this->options['parameters'])) {
+ $this->setError(_s('unknown function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ $num_required_parameters = $this->options['parameters'][$token['data']['function']];
+ $num_parameters = count($token['data']['parameters']);
+
+ if (($num_required_parameters == -1 && $num_parameters == 0)
+ || ($num_required_parameters != -1 && $num_parameters != $num_required_parameters)) {
+ $this->setError(_s('invalid number of parameters in function "%1$s"', $token['data']['function']));
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index cb09c3b4954..58cc6a34385 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -21,7 +21,8 @@
define('ZABBIX_VERSION', '5.4.0rc1');
define('ZABBIX_API_VERSION', '5.4.0');
define('ZABBIX_EXPORT_VERSION', '5.4');
-define('ZABBIX_DB_VERSION', 5030183);
+
+define('ZABBIX_DB_VERSION', 5030187);
define('ZABBIX_COPYRIGHT_FROM', '2001');
define('ZABBIX_COPYRIGHT_TO', '2021');
@@ -516,7 +517,7 @@ define('ITEM_TYPE_SNMPV2C', 4); // Deprecated. Now only used in XML converters
define('ITEM_TYPE_INTERNAL', 5);
define('ITEM_TYPE_SNMPV3', 6); // Deprecated. Now only used in XML converters. Use ITEM_TYPE_SNMP instead.
define('ITEM_TYPE_ZABBIX_ACTIVE', 7);
-define('ITEM_TYPE_AGGREGATE', 8);
+define('ITEM_TYPE_AGGREGATE', 8); // Deprecated. Now only used in XML converters. Use ITEM_TYPE_CALCULATED instead.
define('ITEM_TYPE_HTTPTEST', 9);
define('ITEM_TYPE_EXTERNAL', 10);
define('ITEM_TYPE_DB_MONITOR', 11);
@@ -1265,6 +1266,8 @@ define('ZBX_PREG_ITEM_KEY_FORMAT', '([0-9a-zA-Z_\. \-]+? # match key
))*? # matches non comma separated brackets with parameters zero or more times
)');
+define('TRIGGER_QUERY_PLACEHOLDER', '$'); // !!! Don't forget sync code with C !!!
+
define('ZBX_USER_ONLINE_TIME', 600); // 10min
define('ZBX_GUEST_USER','guest');
diff --git a/ui/include/forms.inc.php b/ui/include/forms.inc.php
index e14fb588dba..b638e886180 100644
--- a/ui/include/forms.inc.php
+++ b/ui/include/forms.inc.php
@@ -854,11 +854,9 @@ function getItemFormData(array $item = [], array $options = []) {
// types, http items only for internal processes
$data['types'] = item_type2str();
unset($data['types'][ITEM_TYPE_HTTPTEST]);
+
if ($data['is_discovery_rule']) {
- unset($data['types'][ITEM_TYPE_AGGREGATE],
- $data['types'][ITEM_TYPE_CALCULATED],
- $data['types'][ITEM_TYPE_SNMPTRAP]
- );
+ unset($data['types'][ITEM_TYPE_CALCULATED], $data['types'][ITEM_TYPE_SNMPTRAP]);
}
// item
@@ -1839,30 +1837,32 @@ function getTriggerFormData(array $data) {
// Trigger expression constructor.
if ($data['expression_constructor'] == IM_TREE) {
- $analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION);
+ $analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION, $error);
if ($analyze !== false) {
list($data['expression_formula'], $data['expression_tree']) = $analyze;
if ($data['expression_action'] !== '' && $data['expression_tree'] !== null) {
$new_expr = remakeExpression($data['expression'], $_REQUEST['expr_target_single'],
- $data['expression_action'], $data['expr_temp']
+ $data['expression_action'], $data['expr_temp'], $error
);
if ($new_expr !== false) {
$data['expression'] = $new_expr;
- $analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION);
+ $analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION, $error);
if ($analyze !== false) {
list($data['expression_formula'], $data['expression_tree']) = $analyze;
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Expression syntax error.'));
}
$data['expr_temp'] = '';
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Expression syntax error.'));
}
}
@@ -1872,6 +1872,7 @@ function getTriggerFormData(array $data) {
$data['expression_field_readonly'] = true;
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Expression syntax error.'));
$data['expression_field_name'] = 'expression';
$data['expression_field_value'] = $data['expression'];
@@ -1887,30 +1888,32 @@ function getTriggerFormData(array $data) {
// Trigger recovery expression constructor.
if ($data['recovery_expression_constructor'] == IM_TREE) {
- $analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION);
+ $analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION, $error);
if ($analyze !== false) {
list($data['recovery_expression_formula'], $data['recovery_expression_tree']) = $analyze;
if ($data['recovery_expression_action'] !== '' && $data['recovery_expression_tree'] !== null) {
$new_expr = remakeExpression($data['recovery_expression'], $_REQUEST['recovery_expr_target_single'],
- $data['recovery_expression_action'], $data['recovery_expr_temp']
+ $data['recovery_expression_action'], $data['recovery_expr_temp'], $error
);
if ($new_expr !== false) {
$data['recovery_expression'] = $new_expr;
- $analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION);
+ $analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION, $error);
if ($analyze !== false) {
list($data['recovery_expression_formula'], $data['recovery_expression_tree']) = $analyze;
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Recovery expression syntax error.'));
}
$data['recovery_expr_temp'] = '';
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Recovery expression syntax error.'));
}
}
@@ -1920,6 +1923,7 @@ function getTriggerFormData(array $data) {
$data['recovery_expression_field_readonly'] = true;
}
else {
+ error(_s('Cannot build expression tree: %1$s.', $error));
show_messages(false, '', _('Recovery expression syntax error.'));
$data['recovery_expression_field_name'] = 'recovery_expression';
$data['recovery_expression_field_value'] = $data['recovery_expression'];
diff --git a/ui/include/items.inc.php b/ui/include/items.inc.php
index 47eddc05599..f7f362e0f40 100644
--- a/ui/include/items.inc.php
+++ b/ui/include/items.inc.php
@@ -90,7 +90,6 @@ function item_type2str($type = null) {
ITEM_TYPE_SNMPTRAP => _('SNMP trap'),
ITEM_TYPE_INTERNAL => _('Zabbix internal'),
ITEM_TYPE_TRAPPER => _('Zabbix trapper'),
- ITEM_TYPE_AGGREGATE => _('Zabbix aggregate'),
ITEM_TYPE_EXTERNAL => _('External check'),
ITEM_TYPE_DB_MONITOR => _('Database monitor'),
ITEM_TYPE_HTTPAGENT => _('HTTP agent'),
@@ -2051,7 +2050,6 @@ function checkNowAllowedTypes() {
ITEM_TYPE_ZABBIX,
ITEM_TYPE_SIMPLE,
ITEM_TYPE_INTERNAL,
- ITEM_TYPE_AGGREGATE,
ITEM_TYPE_EXTERNAL,
ITEM_TYPE_DB_MONITOR,
ITEM_TYPE_IPMI,
diff --git a/ui/include/schema.inc.php b/ui/include/schema.inc.php
index fdb4b77aad0..9a834cb2fbe 100644
--- a/ui/include/schema.inc.php
+++ b/ui/include/schema.inc.php
@@ -2742,6 +2742,12 @@ return [
'type' => DB::FIELD_TYPE_CHAR,
'length' => 32,
'default' => '60s'
+ ],
+ 'dbversion_status' => [
+ 'null' => false,
+ 'type' => DB::FIELD_TYPE_CHAR,
+ 'length' => 1024,
+ 'default' => ''
]
]
],
diff --git a/ui/include/triggers.inc.php b/ui/include/triggers.inc.php
index 98d21e37275..e2126606294 100644
--- a/ui/include/triggers.inc.php
+++ b/ui/include/triggers.inc.php
@@ -580,7 +580,7 @@ function copyTriggersToHosts($src_triggerids, $dst_hostids, $src_hostid = null)
/**
* Purpose: Replaces host in trigger expression.
- * {localhost:agent.ping.nodata(5m)} => {localhost6:agent.ping.nodata(5m)}
+ * nodata(/localhost/agent.ping, 5m) => nodata(/localhost6/agent.ping, 5m)
*
* @param string $expression full expression with host names and item keys
* @param string $src_host
@@ -588,75 +588,26 @@ function copyTriggersToHosts($src_triggerids, $dst_hostids, $src_hostid = null)
*
* @return string
*/
-function triggerExpressionReplaceHost($expression, $src_host, $dst_host) {
- $new_expression = '';
-
- $function_macro_parser = new CFunctionMacroParser();
- $user_macro_parser = new CUserMacroParser();
- $macro_parser = new CMacroParser(['macros' => ['{TRIGGER.VALUE}']]);
- $lld_macro_parser = new CLLDMacroParser();
- $lld_macro_function_parser = new CLLDMacroFunctionParser();
-
- for ($pos = 0, $pos_left = 0; isset($expression[$pos]); $pos++) {
- if ($function_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
- $host = $function_macro_parser->getHost();
- $item = $function_macro_parser->getItem();
- $function = $function_macro_parser->getFunction();
-
- if ($host === $src_host) {
- $host = $dst_host;
+function triggerExpressionReplaceHost(string $expression, string $src_host, string $dst_host): string {
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+
+ if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
+ $hist_functions = $expression_parser->getResult()->getTokensOfTypes(
+ [CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION]
+ );
+ $hist_function = end($hist_functions);
+ do {
+ $query_parameter = $hist_function['data']['parameters'][0];
+ if ($query_parameter['data']['host'] === $src_host) {
+ $expression = substr_replace($expression, '/'.$dst_host.'/'.$query_parameter['data']['item'],
+ $query_parameter['pos'], $query_parameter['length']
+ );
}
-
- $new_expression .= substr($expression, $pos_left, $pos - $pos_left);
- $new_expression .= '{'.$host.':'.$item.'.'.$function.'}';
- $pos_left = $pos + $function_macro_parser->getLength();
-
- $pos += $function_macro_parser->getLength() - 1;
- }
- elseif ($user_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
- $pos += $user_macro_parser->getLength() - 1;
- }
- elseif ($macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
- $pos += $macro_parser->getLength() - 1;
- }
- elseif ($lld_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
- $pos += $lld_macro_parser->getLength() - 1;
- }
- elseif ($lld_macro_function_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
- $pos += $lld_macro_function_parser->getLength() - 1;
- }
- }
-
- $new_expression .= substr($expression, $pos_left, $pos - $pos_left);
-
- return $new_expression;
-}
-
-function check_right_on_trigger_by_expression($permission, $expression) {
- $expressionData = new CTriggerExpression();
- if (!$expressionData->parse($expression)) {
- error($expressionData->error);
- return false;
- }
- $expressionHosts = $expressionData->getHosts();
-
- $hosts = API::Host()->get([
- 'filter' => ['host' => $expressionHosts],
- 'editable' => ($permission == PERM_READ_WRITE),
- 'output' => ['hostid', 'host'],
- 'templated_hosts' => true,
- 'preservekeys' => true
- ]);
- $hosts = zbx_toHash($hosts, 'host');
-
- foreach ($expressionHosts as $host) {
- if (!isset($hosts[$host])) {
- error(_s('Incorrect trigger expression. Host "%1$s" does not exist or you have no access to this host.', $host));
- return false;
}
+ while ($hist_function = prev($hist_functions));
}
- return true;
+ return $expression;
}
function replace_template_dependencies($deps, $hostid) {
@@ -1235,22 +1186,24 @@ function make_trigger_details($trigger, $eventid) {
*
* @param string $expression Trigger expression or recovery expression string.
* @param int $type Type can be either TRIGGER_EXPRESSION or TRIGGER_RECOVERY_EXPRESSION.
+ * @param string $error [OUT] An error message.
*
* @return array|bool
*/
-function analyzeExpression($expression, $type) {
+function analyzeExpression(string $expression, int $type, string &$error = null) {
if ($expression === '') {
return ['', null];
}
- $expression_data = new CTriggerExpression();
- if (!$expression_data->parse($expression)) {
- error($expression_data->error);
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+
+ if ($expression_parser->parse($expression) != CParser::PARSE_SUCCESS) {
+ $error = $expression_parser->getError();
return false;
}
- $expression_tree[] = getExpressionTree($expression_data, 0, strlen($expression_data->expression) - 1);
+ $expression_tree[] = getExpressionTree($expression_parser, 0, $expression_parser->getLength() - 1);
$next = [];
$letter_num = 0;
@@ -1295,7 +1248,7 @@ function buildExpressionHtmlTree(array $expressionTree, array &$next, &$letterNu
];
$levelErrors = expressionHighLevelErrors($element['expression']);
- if (count($levelErrors) > 0) {
+ if ($levelErrors) {
$levelDetails['expression']['levelErrors'] = $levelErrors;
}
$treeList[] = $levelDetails;
@@ -1350,7 +1303,7 @@ function buildExpressionHtmlTree(array $expressionTree, array &$next, &$letterNu
];
$levelErrors = expressionHighLevelErrors($element['expression']);
- if (count($levelErrors) > 0) {
+ if ($levelErrors) {
$levelDetails['expression']['levelErrors'] = $levelErrors;
}
$treeList[] = $levelDetails;
@@ -1377,14 +1330,18 @@ function expressionHighLevelErrors($expression) {
if (!isset($errors[$expression])) {
$errors[$expression] = [];
- $expressionData = new CTriggerExpression();
- if ($expressionData->parse($expression)) {
- foreach ($expressionData->expressions as $exprPart) {
- $info = get_item_function_info($exprPart['expression']);
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes([
+ CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION
+ ]);
+ foreach ($tokens as $token) {
+ $info = get_item_function_info($token['match']);
if (!is_array($info) && isset($definedErrorPhrases[$info])) {
- if (!isset($errors[$expression][$exprPart['expression']])) {
- $errors[$expression][$exprPart['expression']] = $definedErrorPhrases[$info];
+ if (!isset($errors[$expression][$token['match']])) {
+ $errors[$expression][$token['match']] = $definedErrorPhrases[$info];
}
}
}
@@ -1392,18 +1349,23 @@ function expressionHighLevelErrors($expression) {
}
$ret = [];
- if (count($errors[$expression]) == 0) {
+ if (!$errors[$expression]) {
return $ret;
}
- $expressionData = new CTriggerExpression();
- if ($expressionData->parse($expression)) {
- foreach ($expressionData->expressions as $exprPart) {
- if (isset($errors[$expression][$exprPart['expression']])) {
- $ret[$exprPart['expression']] = $errors[$expression][$exprPart['expression']];
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ if ($expression_parser->parse($expression) == CParser::PARSE_SUCCESS) {
+ $tokens = $expression_parser->getResult()->getTokensOfTypes([
+ CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION
+ ]);
+ foreach ($tokens as $token) {
+ if (isset($errors[$expression][$token['match']])) {
+ $ret[$token['match']] = $errors[$expression][$token['match']];
}
}
}
+
return $ret;
}
@@ -1434,36 +1396,37 @@ function expressionLevelDraw(array $next, $level) {
* Makes tree of expression elements
*
* Expression:
- * "{host1:system.cpu.util[,iowait].last(0)} > 50 and {host2:system.cpu.util[,iowait].last(0)} > 50"
+ * "last(/host1/system.cpu.util[,iowait], 0) > 50 and last(/host2/system.cpu.util[,iowait], 0) > 50"
* Result:
- * array(
- * [0] => array(
+ * [
+ * [0] => [
* 'id' => '0_94',
* 'type' => 'operator',
* 'operator' => 'and',
- * 'elements' => array(
- * [0] => array(
+ * 'elements' => [
+ * [0] => [
* 'id' => '0_44',
* 'type' => 'expression',
- * 'expression' => '{host1:system.cpu.util[,iowait].last(0)} > 50'
- * ),
- * [1] => array(
+ * 'expression' => 'last(/host1/system.cpu.util[,iowait], 0) > 50'
+ * ],
+ * [1] => [
* 'id' => '50_94',
* 'type' => 'expression',
- * 'expression' => '{host2:system.cpu.util[,iowait].last(0)} > 50'
- * )
- * )
- * )
- * )
+ * 'expression' => 'last(/host2/system.cpu.util[,iowait], 0) > 50'
+ * ]
+ * ]
+ * ]
+ * ]
*
- * @param CTriggerExpression $expressionData
+ * @param CExpressionParser $expression_parser
* @param int $start
* @param int $end
*
* @return array
*/
-function getExpressionTree(CTriggerExpression $expressionData, $start, $end) {
- $blankSymbols = [' ', "\r", "\n", "\t"];
+function getExpressionTree(CExpressionParser $expression_parser, int $start, int $end) {
+ $tokens = array_column($expression_parser->getResult()->getTokens(), null, 'pos');
+ $expression = $expression_parser->getMatch();
$expressionTree = [];
foreach (['or', 'and'] as $operator) {
@@ -1472,11 +1435,9 @@ function getExpressionTree(CTriggerExpression $expressionData, $start, $end) {
$rParentheses = -1;
$expressions = [];
$openSymbolNum = $start;
- $operatorPos = 0;
- $operatorToken = '';
for ($i = $start, $level = 0; $i <= $end; $i++) {
- switch ($expressionData->expression[$i]) {
+ switch ($expression[$i]) {
case ' ':
case "\r":
case "\n":
@@ -1485,105 +1446,92 @@ function getExpressionTree(CTriggerExpression $expressionData, $start, $end) {
$openSymbolNum++;
}
break;
+
case '(':
if ($level == 0) {
$lParentheses = $i;
}
$level++;
break;
+
case ')':
$level--;
if ($level == 0) {
$rParentheses = $i;
}
break;
- case '{':
- case '"':
- // Skip any previously found tokens starting with brace or double quote.
- foreach ($expressionData->result->getTokens() as $expression_token) {
- if ($expression_token['pos'] == $i) {
- $i += $expression_token['length'] - 1;
- break;
- }
- }
- break;
- default:
- // try to parse an operator
- if ($operator[$operatorPos] === $expressionData->expression[$i]) {
- $operatorPos++;
- $operatorToken .= $expressionData->expression[$i];
-
- // operator found
- if ($operatorToken === $operator) {
- // we've reached the end of a complete expression, parse the expression on the left side of
- // the operator
- if ($level == 0) {
- // find the last symbol of the expression before the operator
- $closeSymbolNum = $i - strlen($operator);
-
- // trim blank symbols after the expression
- while (in_array($expressionData->expression[$closeSymbolNum], $blankSymbols)) {
- $closeSymbolNum--;
- }
- $expressions[] = getExpressionTree($expressionData, $openSymbolNum, $closeSymbolNum);
- $openSymbolNum = $i + 1;
- $operatorFound = true;
- }
- $operatorPos = 0;
- $operatorToken = '';
+ default:
+ /*
+ * Once reached the end of a complete expression, parse the expression on the left side of the
+ * operator.
+ */
+ if ($level == 0 && array_key_exists($i, $tokens)
+ && $tokens[$i]['type'] == CExpressionParserResult::TOKEN_TYPE_OPERATOR
+ && $tokens[$i]['match'] === $operator) {
+ // Find the last symbol of the expression before the operator.
+ $closeSymbolNum = $i - 1;
+
+ // Trim blank symbols after the expression.
+ while (strpos(CExpressionParser::WHITESPACES, $expression[$closeSymbolNum]) !== false) {
+ $closeSymbolNum--;
}
+
+ $expressions[] = getExpressionTree($expression_parser, $openSymbolNum, $closeSymbolNum);
+ $openSymbolNum = $i + $tokens[$i]['length'];
+ $operatorFound = true;
}
}
}
- // trim blank symbols in the end of the trigger expression
+ // Trim blank symbols in the end of the trigger expression.
$closeSymbolNum = $end;
- while (in_array($expressionData->expression[$closeSymbolNum], $blankSymbols)) {
+ while (strpos(CExpressionParser::WHITESPACES, $expression[$closeSymbolNum]) !== false) {
$closeSymbolNum--;
}
- // we've found a whole expression and parsed the expression on the left side of the operator,
- // parse the expression on the right
+ /*
+ * Once found a whole expression and parsed the expression on the left side of the operator, parse the
+ * expression on the right.
+ */
if ($operatorFound) {
- $expressions[] = getExpressionTree($expressionData, $openSymbolNum, $closeSymbolNum);
+ $expressions[] = getExpressionTree($expression_parser, $openSymbolNum, $closeSymbolNum);
- // trim blank symbols in the beginning of the trigger expression
+ // Trim blank symbols in the beginning of the trigger expression.
$openSymbolNum = $start;
- while (in_array($expressionData->expression[$openSymbolNum], $blankSymbols)) {
+ while (strpos(CExpressionParser::WHITESPACES, $expression[$openSymbolNum]) !== false) {
$openSymbolNum++;
}
- // trim blank symbols in the end of the trigger expression
+ // Trim blank symbols in the end of the trigger expression.
$closeSymbolNum = $end;
- while (in_array($expressionData->expression[$closeSymbolNum], $blankSymbols)) {
+ while (strpos(CExpressionParser::WHITESPACES, $expression[$closeSymbolNum]) !== false) {
$closeSymbolNum--;
}
$expressionTree = [
'id' => $openSymbolNum.'_'.$closeSymbolNum,
- 'expression' => substr($expressionData->expression, $openSymbolNum, $closeSymbolNum - $openSymbolNum + 1),
+ 'expression' => substr($expression, $openSymbolNum, $closeSymbolNum - $openSymbolNum + 1),
'type' => 'operator',
'operator' => $operator,
'elements' => $expressions
];
break;
}
- // if we've tried both operators and didn't find anything, it means there's only one expression
- // return the result
+ // If finding both operators failed, it means there's only one expression return the result.
elseif ($operator === 'and') {
- // trim extra parentheses
+ // Trim extra parentheses.
if ($openSymbolNum == $lParentheses && $closeSymbolNum == $rParentheses) {
$openSymbolNum++;
$closeSymbolNum--;
- $expressionTree = getExpressionTree($expressionData, $openSymbolNum, $closeSymbolNum);
+ $expressionTree = getExpressionTree($expression_parser, $openSymbolNum, $closeSymbolNum);
}
- // no extra parentheses remain, return the result
+ // No extra parentheses remain, return the result.
else {
$expressionTree = [
'id' => $openSymbolNum.'_'.$closeSymbolNum,
- 'expression' => substr($expressionData->expression, $openSymbolNum, $closeSymbolNum - $openSymbolNum + 1),
+ 'expression' => substr($expression, $openSymbolNum, $closeSymbolNum - $openSymbolNum + 1),
'type' => 'expression'
];
}
@@ -1606,28 +1554,27 @@ function getExpressionTree(CTriggerExpression $expressionData, $start, $end) {
* @param string $expression_id Element identifier like "0_55".
* @param string $action Action to perform.
* @param string $new_expression Expression for AND, OR or replace actions.
+ * @param string $error [OUT] An error message.
*
* @return bool|string Returns new expression or false if expression is incorrect.
*/
-function remakeExpression($expression, $expression_id, $action, $new_expression) {
+function remakeExpression($expression, $expression_id, $action, $new_expression, string &$error = null) {
if ($expression === '') {
return false;
}
- $expression_data = new CTriggerExpression();
- if ($action !== 'R' && !$expression_data->parse($new_expression)) {
- error($expression_data->error);
-
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ if ($action !== 'R' && $expression_parser->parse($new_expression) != CParser::PARSE_SUCCESS) {
+ $error = $expression_parser->getError();
return false;
}
- if (!$expression_data->parse($expression)) {
- error($expression_data->error);
-
+ if ($expression_parser->parse($expression) != CParser::PARSE_SUCCESS) {
+ $error = $expression_parser->getError();
return false;
}
- $expression_tree[] = getExpressionTree($expression_data, 0, strlen($expression_data->expression) - 1);
+ $expression_tree[] = getExpressionTree($expression_parser, 0, $expression_parser->getLength() - 1);
if (rebuildExpressionTree($expression_tree, $expression_id, $action, $new_expression)) {
$expression = makeExpression($expression_tree);
@@ -1820,7 +1767,7 @@ function makeExpression(array $expressionTree, $level = 0, $operator = null) {
return $expression;
}
-function get_item_function_info($expr) {
+function get_item_function_info(string $expr) {
$rule_float = ['value_type' => _('Numeric (float)'), 'values' => null];
$rule_int = ['value_type' => _('Numeric (integer)'), 'values' => null];
$rule_str = ['value_type' => _('String'), 'values' => null];
@@ -1867,136 +1814,139 @@ function get_item_function_info($expr) {
],
'log_as_0or1' => [
ITEM_VALUE_TYPE_LOG => $rule_0or1
- ],
- 'date' => [
- 'any' => ['value_type' => 'YYYYMMDD', 'values' => null]
- ],
- 'time' => [
- 'any' => ['value_type' => 'HHMMSS', 'values' => null]
- ],
- 'day_of_month' => [
- 'any' => ['value_type' => '1-31', 'values' => null]
- ],
- 'day_of_week' => [
- 'any' => ['value_type' => '1-7', 'values' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7]]
]
];
- $functions = [
- 'abschange' => $rules['numeric'] + $rules['string_as_0or1'],
+ $hist_functions = [
'avg' => $rules['numeric_as_float'],
- 'band' => $rules['integer'],
- 'change' => $rules['numeric'] + $rules['string_as_0or1'],
'count' => $rules['numeric_as_uint'] + $rules['string_as_uint'],
- 'date' => $rules['date'],
- 'dayofmonth' => $rules['day_of_month'],
- 'dayofweek' => $rules['day_of_week'],
- 'delta' => $rules['numeric'],
- 'diff' => $rules['numeric_as_0or1'] + $rules['string_as_0or1'],
+ 'change' => $rules['numeric'] + $rules['string_as_0or1'],
+ 'find' => $rules['numeric_as_0or1'] + $rules['string_as_0or1'],
'forecast' => $rules['numeric_as_float'],
'fuzzytime' => $rules['numeric_as_0or1'],
- 'iregexp' => $rules['string_as_0or1'],
'last' => $rules['numeric'] + $rules['string'],
+ 'length' => $rules['numeric'] + $rules['string'],
'logeventid' => $rules['log_as_0or1'],
'logseverity' => $rules['log_as_uint'],
'logsource' => $rules['log_as_0or1'],
'max' => $rules['numeric'],
'min' => $rules['numeric'],
'nodata' => $rules['numeric_as_0or1'] + $rules['string_as_0or1'],
- 'now' => $rules['numeric_as_uint'] + $rules['string_as_uint'],
'percentile' => $rules['numeric'],
- 'prev' => $rules['numeric'] + $rules['string'],
- 'regexp' => $rules['string_as_0or1'],
- 'str' => $rules['string_as_0or1'],
- 'strlen' => $rules['string_as_uint'],
'sum' => $rules['numeric'],
- 'time' => $rules['time'],
'timeleft' => $rules['numeric_as_float'],
'trendavg' => $rules['numeric'],
'trendcount' => $rules['numeric'],
- 'trenddelta' => $rules['numeric'],
'trendmax' => $rules['numeric'],
'trendmin' => $rules['numeric'],
'trendsum' => $rules['numeric']
];
- $expr_data = new CTriggerExpression();
- $expression = $expr_data->parse($expr);
+ $math_functions = [
+ 'abs' => ['any' => $rule_float],
+ 'avg' => ['any' => $rule_float],
+ 'bitand' => ['any' => $rule_int],
+ 'date' => [
+ 'any' => ['value_type' => 'YYYYMMDD', 'values' => null]
+ ],
+ 'dayofmonth' => [
+ 'any' => ['value_type' => '1-31', 'values' => null]
+ ],
+ 'dayofweek' => [
+ 'any' => ['value_type' => '1-7', 'values' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7]]
+ ],
+ 'length' => ['any' => $rule_int],
+ 'max' => ['any' => $rule_float],
+ 'min' => ['any' => $rule_float],
+ 'now' => ['any' => $rule_int],
+ 'sum' => ['any' => $rule_float],
+ 'time' => [
+ 'any' => ['value_type' => 'HHMMSS', 'values' => null]
+ ]
+ ];
- if (!$expression) {
- return EXPRESSION_NOT_A_MACRO_ERROR;
- }
+ $expression_parser = new CExpressionParser(['lldmacros' => true]);
+ $expression_parser->parse($expr);
+ $token = $expression_parser->getResult()->getTokens()[0];
- switch (true) {
- case ($expression->hasTokenOfType(CTriggerExprParserResult::TOKEN_TYPE_MACRO)):
+ switch ($token['type']) {
+ case CExpressionParserResult::TOKEN_TYPE_MACRO:
$result = $rule_0or1;
break;
- case ($expression->hasTokenOfType(CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO)):
- case ($expression->hasTokenOfType(CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO)):
+ case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
+ case CExpressionParserResult::TOKEN_TYPE_LLD_MACRO:
$result = $rule_any;
break;
- case ($expression->hasTokenOfType(CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO)):
- $expr_part = reset($expr_data->expressions);
-
- if (!array_key_exists($expr_part['functionName'], $functions)) {
+ case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
+ if (!array_key_exists($token['data']['function'], $hist_functions)) {
$result = EXPRESSION_FUNCTION_UNKNOWN;
break;
}
- $host = API::Host()->get([
+ $hosts = API::Host()->get([
'output' => ['hostid'],
- 'filter' => ['host' => [$expr_part['host']]],
+ 'filter' => [
+ 'host' => $token['data']['parameters'][0]['data']['host']
+ ],
'templated_hosts' => true
]);
- if (!$host) {
+ if (!$hosts) {
$result = EXPRESSION_HOST_UNKNOWN;
break;
}
- $item = API::Item()->get([
+ $items = API::Item()->get([
'output' => ['value_type'],
- 'hostids' => $host[0]['hostid'],
+ 'hostids' => $hosts[0]['hostid'],
'filter' => [
- 'key_' => [$expr_part['item']]
+ 'key_' => $token['data']['parameters'][0]['data']['item']
],
'webitems' => true
]);
- if (!$item) {
- $item = API::ItemPrototype()->get([
+ if (!$items) {
+ $items = API::ItemPrototype()->get([
'output' => ['value_type'],
- 'hostids' => $host[0]['hostid'],
+ 'hostids' => $hosts[0]['hostid'],
'filter' => [
- 'key_' => [$expr_part['item']]
+ 'key_' => $token['data']['parameters'][0]['data']['item']
]
]);
}
- if (!$item) {
+ if (!$items) {
$result = EXPRESSION_HOST_ITEM_UNKNOWN;
break;
}
- $function = $functions[$expr_part['functionName']];
- $value_type = $item[0]['value_type'];
+ $hist_function = $hist_functions[$token['data']['function']];
+ $value_type = $items[0]['value_type'];
- if (array_key_exists('any', $function)) {
+ if (array_key_exists('any', $hist_function)) {
$value_type = 'any';
}
- elseif (!array_key_exists($value_type, $function)) {
+ elseif (!array_key_exists($value_type, $hist_function)) {
$result = EXPRESSION_UNSUPPORTED_VALUE_TYPE;
break;
}
- $result = $function[$value_type];
+ $result = $hist_function[$value_type];
+ break;
+
+ case CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION:
+ if (!array_key_exists($token['data']['function'], $math_functions)) {
+ $result = EXPRESSION_FUNCTION_UNKNOWN;
+ break;
+ }
+
+ $result = $math_functions[$token['data']['function']]['any'];
break;
default:
$result = EXPRESSION_NOT_A_MACRO_ERROR;
- break;
}
return $result;
@@ -2581,3 +2531,12 @@ function makeTriggerDependencies(array $dependencies, $freeze_on_click = true) {
return $result;
}
+
+/**
+ * Return list of functions that can be used without /host/key reference.
+ *
+ * @return array
+ */
+function getStandaloneFunctions(): array {
+ return ['date', 'dayofmonth', 'dayofweek', 'time', 'now'];
+}
diff --git a/ui/include/views/configuration.host.discovery.list.php b/ui/include/views/configuration.host.discovery.list.php
index 297a2b01256..b02bd74bee7 100644
--- a/ui/include/views/configuration.host.discovery.list.php
+++ b/ui/include/views/configuration.host.discovery.list.php
@@ -115,9 +115,7 @@ zbx_subarray_push($filter_type_visibility, -1, 'filter_delay_row');
zbx_subarray_push($filter_type_visibility, -1, 'filter_delay');
$lld_types = item_type2str();
-unset($lld_types[ITEM_TYPE_AGGREGATE], $lld_types[ITEM_TYPE_HTTPTEST], $lld_types[ITEM_TYPE_CALCULATED],
- $lld_types[ITEM_TYPE_SNMPTRAP]
-);
+unset($lld_types[ITEM_TYPE_HTTPTEST], $lld_types[ITEM_TYPE_CALCULATED], $lld_types[ITEM_TYPE_SNMPTRAP]);
$type_select->addOptions(CSelect::createOptionsFromArray($lld_types));
diff --git a/ui/include/views/js/common.item.edit.js.php b/ui/include/views/js/common.item.edit.js.php
index be4652f5908..f4f8115cbea 100644
--- a/ui/include/views/js/common.item.edit.js.php
+++ b/ui/include/views/js/common.item.edit.js.php
@@ -199,17 +199,12 @@ foreach ($this->data['types'] as $type => $label) {
zbx_subarray_push($this->data['typeVisibility'], $type, 'row_delay');
}
-// disable dropdown items for calculated and aggregate items
-foreach ([ITEM_TYPE_CALCULATED, ITEM_TYPE_AGGREGATE] as $type) {
- // set to disable character, log and text items in value type
- zbx_subarray_push($this->data['typeDisable'], $type, [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT], 'value_type');
-}
-
$this->data['authTypeVisibility'] = [];
zbx_subarray_push($this->data['authTypeVisibility'], ITEM_AUTHTYPE_PUBLICKEY, 'publickey');
zbx_subarray_push($this->data['authTypeVisibility'], ITEM_AUTHTYPE_PUBLICKEY, 'row_publickey');
zbx_subarray_push($this->data['authTypeVisibility'], ITEM_AUTHTYPE_PUBLICKEY, 'privatekey');
zbx_subarray_push($this->data['authTypeVisibility'], ITEM_AUTHTYPE_PUBLICKEY, 'row_privatekey');
+zbx_subarray_push($this->data['typeDisable'], ITEM_TYPE_CALCULATED, [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT], 'value_type');
?>
<script type="text/javascript">
diff --git a/ui/include/views/js/configuration.item.edit.js.php b/ui/include/views/js/configuration.item.edit.js.php
index 8c2fbbc410a..6755f5798b4 100644
--- a/ui/include/views/js/configuration.item.edit.js.php
+++ b/ui/include/views/js/configuration.item.edit.js.php
@@ -72,7 +72,6 @@ zbx_subarray_push($this->data['valueTypeVisibility'], ITEM_VALUE_TYPE_UINT64, 'r
&& type != <?= ITEM_TYPE_ZABBIX_ACTIVE ?>
&& type != <?= ITEM_TYPE_SIMPLE ?>
&& type != <?= ITEM_TYPE_INTERNAL ?>
- && type != <?= ITEM_TYPE_AGGREGATE ?>
&& type != <?= ITEM_TYPE_DB_MONITOR ?>
&& type != <?= ITEM_TYPE_SNMPTRAP ?>
&& type != <?= ITEM_TYPE_JMX ?>
@@ -105,7 +104,7 @@ zbx_subarray_push($this->data['valueTypeVisibility'], ITEM_VALUE_TYPE_UINT64, 'r
var type = $(this).val();
old_value = value_type.val();
- if (type == <?= ITEM_TYPE_AGGREGATE ?> || type == <?= ITEM_TYPE_CALCULATED ?>) {
+ if (type == <?= ITEM_TYPE_CALCULATED ?>) {
if (!(old_value == <?= ITEM_VALUE_TYPE_UINT64 ?> || old_value == <?= ITEM_VALUE_TYPE_FLOAT ?>)) {
value_type.val(<?= ITEM_VALUE_TYPE_UINT64 ?>);
}
diff --git a/ui/include/views/js/configuration.item.prototype.edit.js.php b/ui/include/views/js/configuration.item.prototype.edit.js.php
index 9b6258fd658..c64fb5440ef 100644
--- a/ui/include/views/js/configuration.item.prototype.edit.js.php
+++ b/ui/include/views/js/configuration.item.prototype.edit.js.php
@@ -64,7 +64,6 @@ zbx_subarray_push($this->data['valueTypeVisibility'], ITEM_VALUE_TYPE_UINT64, 'v
&& type != <?= ITEM_TYPE_ZABBIX_ACTIVE ?>
&& type != <?= ITEM_TYPE_SIMPLE ?>
&& type != <?= ITEM_TYPE_INTERNAL ?>
- && type != <?= ITEM_TYPE_AGGREGATE ?>
&& type != <?= ITEM_TYPE_DB_MONITOR ?>
&& type != <?= ITEM_TYPE_SNMPTRAP ?>
&& type != <?= ITEM_TYPE_JMX ?>
diff --git a/ui/include/views/js/itemtest.js.php b/ui/include/views/js/itemtest.js.php
index 7d5dad08657..a95aab57510 100644
--- a/ui/include/views/js/itemtest.js.php
+++ b/ui/include/views/js/itemtest.js.php
@@ -122,7 +122,6 @@
break;
case <?= ITEM_TYPE_INTERNAL ?>:
- case <?= ITEM_TYPE_AGGREGATE ?>:
case <?= ITEM_TYPE_EXTERNAL ?>:
properties = {
key: form_data['key'].trim()
diff --git a/ui/items.php b/ui/items.php
index 9ebac938099..df31edb882a 100644
--- a/ui/items.php
+++ b/ui/items.php
@@ -71,8 +71,8 @@ $fields = [
'status' => [T_ZBX_INT, O_OPT, null, IN([ITEM_STATUS_DISABLED, ITEM_STATUS_ACTIVE]), null],
'type' => [T_ZBX_INT, O_OPT, null,
IN([-1, ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE,
- ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE,
- ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
+ ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL,
+ ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMPTRAP,
ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
]),
@@ -247,7 +247,7 @@ $fields = [
'filter_name' => [T_ZBX_STR, O_OPT, null, null, null],
'filter_type' => [T_ZBX_INT, O_OPT, null,
IN([-1, ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE,
- ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE,
+ ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMPTRAP,
ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
@@ -1415,8 +1415,8 @@ else {
if ($filter_delay !== '') {
if ($filter_type == -1 && $filter_delay == 0) {
$options['filter']['type'] = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR,
- ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX
+ ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI,
+ ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX
];
$options['filter']['delay'] = $filter_delay;
diff --git a/ui/js/init.js b/ui/js/init.js
index a53f9798876..57355b5bbb4 100644
--- a/ui/js/init.js
+++ b/ui/js/init.js
@@ -346,7 +346,8 @@ jQuery(function($) {
if (typeof data.values[i].id !== 'undefined') {
var item = {
'id': data.values[i].id,
- 'name': data.values[i].name
+ 'name': data.values[i].name,
+ 'query': data.values[i].query
};
if (typeof data.values[i].prefix !== 'undefined') {
diff --git a/ui/tests/api_json/data/data_test.sql b/ui/tests/api_json/data/data_test.sql
index e6099c65a63..68082409fb1 100644
--- a/ui/tests/api_json/data/data_test.sql
+++ b/ui/tests/api_json/data/data_test.sql
@@ -819,24 +819,24 @@ INSERT INTO interface (interfaceid, hostid, main, type, useip, ip, dns, port) VA
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers, flags) VALUES (40070, 2, 120004, 'discovery_rule', '', 'discovery', '0', NULL, '', '', '', '', '', '', 1);
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers, value_type, flags) VALUES (40071, 2, 120004, 'Item {#NAME}', '', 'item[{#NAME}]', '0', NULL, '', '', '', '', '', '', 3, 2);
INSERT INTO triggers (triggerid, expression, description, priority, flags, comments) VALUES (30001,'{99000}>0','Trigger {#NAME}', 2, 2, '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99000, 40071, 30001, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99000, 40071, 30001, 'last', '$');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, key_) VALUES (14045, 40071, 40070, '');
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers, value_type, flags) VALUES (40072, 2, 120004,' Item eth0', '', 'item[eth0]', '0', NULL, '', '', '', '', '', '', 3, 4);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, key_) VALUES (14046, 40072, 40071, 'item[{#NAME}]');
INSERT INTO triggers (triggerid, expression, description, priority, flags, comments, value) VALUES (30002,'{99001}>0','Trigger eth0', 2, 4, '', 1);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99001, 40072, 30002, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99001, 40072, 30002, 'last', '$');
INSERT INTO trigger_discovery (triggerid, parent_triggerid) VALUES (30002, 30001);
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers, value_type, flags, master_itemid) VALUES (40073, 18, 120004, 'Item_child {#NAME}', '', 'item_child[{#NAME}]', '0', NULL, '', '', '', '', '', '', 3, 2, 40071);
INSERT INTO triggers (triggerid, expression, description, priority, flags, comments) VALUES (30003,'{99002}>0','Trigger {#NAME}', 2, 2, '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99002, 40073, 30003, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99002, 40073, 30003, 'last', '$');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, key_) VALUES (14047, 40073, 40070, '');
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers, value_type, flags, master_itemid) VALUES (40074, 18, 120004,' Item_child eth0', '', 'item_child[eth0]', '0', NULL, '', '', '', '', '', '', 3, 4, 40072);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, key_) VALUES (14048, 40074, 40073, 'item[{#NAME}]');
INSERT INTO triggers (triggerid, expression, description, priority, flags, comments, value) VALUES (30004,'{99003}>0','Trigger eth0', 2, 4, '', 1);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99003, 40074, 30004, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99003, 40074, 30004, 'last', '$');
INSERT INTO trigger_discovery (triggerid, parent_triggerid) VALUES (30004, 30003);
-- LLD rules
@@ -915,24 +915,24 @@ insert into item_discovery (itemdiscoveryid,itemid,parent_itemid,key_) values (1
insert into item_discovery (itemdiscoveryid,itemid,parent_itemid,key_) values (138001,132005,132003,'triggerstesteritemprototmpl[{#T}]');
insert into triggers (triggerid,expression,description,priority,comments) values (134000,'{135000}=0','triggerstester_t0',0,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135000,132000,134000,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135000,132000,134000,'now','$,0');
insert into triggers (triggerid,expression,description,priority,comments) values (134001,'{135001}=0','triggerstester_t1',1,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135001,132000,134001,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135001,132000,134001,'now','$,0');
insert into triggers (triggerid,expression,description,priority,comments) values (134002,'{135002}=0','triggerstester_t2',2,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135002,132000,134002,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135002,132000,134002,'now','$,0');
insert into triggers (triggerid,expression,description,priority,comments) values (134003,'{135003}=0','triggerstester_t3',3,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135003,132000,134003,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135003,132000,134003,'now','$,0');
insert into triggers (triggerid,expression,description,priority,comments) values (134004,'{135004}=0','triggerstester_t4',4,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135004,132000,134004,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135004,132000,134004,'now','$,0');
insert into triggers (triggerid,expression,description,priority,comments) values (134005,'{135005}=0','triggerstester_t5',5,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135005,132000,134005,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135005,132000,134005,'now','$,0');
insert into triggers (triggerid,expression,description,priority,flags,comments) values (134106,'{135106}=0','triggerstesterlld_t0',0,2,'');
-insert into functions (functionid,itemid,triggerid,name,parameter) values (135106,132004,134106,'now','0');
+insert into functions (functionid,itemid,triggerid,name,parameter) values (135106,132004,134106,'now','$,0');
-- discovered
INSERT INTO items (itemid,hostid,type,name,key_,flags,params,description,posts,headers) VALUES (132006,130000,2,'TriggersTesterItemLLDDiscovered[res1]','TriggersTesterItemLLDDiscovered[res1]',4,'','','','');
INSERT INTO triggers (triggerid,expression,description,priority,flags,comments) VALUES (134118,'{135118}=0','TriggersTesterLLDTmpl_T0[res1]',0,4,'');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (135118,132006,134118,'now','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (135118,132006,134118,'now','$,0');
INSERT INTO trigger_discovery (triggerid,parent_triggerid) VALUES (134118,134106);
insert into item_discovery (itemdiscoveryid,itemid,parent_itemid,key_) values (138002,132006,132004,'triggerstesteritemprototmpl[{#T}]');
-- T4 depends on T5 depends on T0 (LLD discovered version)
@@ -1259,206 +1259,206 @@ INSERT INTO items (itemid, hostid, interfaceid, name, type, key_, value_type, de
INSERT INTO item_rtdata (itemid) VALUES (50115);
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50101, 'test-trigger-permissions-trigger-{N}', '{50101}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50101, 50101, 50101, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50101, 50101, 50101, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50102, 'test-trigger-permissions-trigger-{D}', '{50102}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50102, 50102, 50102, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50102, 50102, 50102, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50103, 'test-trigger-permissions-trigger-{R}', '{50103}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50103, 50103, 50103, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50103, 50103, 50103, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50104, 'test-trigger-permissions-trigger-{W}', '{50104}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50104, 50104, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50104, 50104, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50105, 'test-trigger-permissions-trigger-{N}-{D}', '{50105} or {50106}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50105, 50105, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50106, 50105, 50102, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50105, 50105, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50106, 50105, 50102, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50106, 'test-trigger-permissions-trigger-{N}-{R}', '{50107} or {50108}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50107, 50106, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50108, 50106, 50103, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50107, 50106, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50108, 50106, 50103, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50107, 'test-trigger-permissions-trigger-{N}-{W}', '{50109} or {50110}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50109, 50107, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50110, 50107, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50109, 50107, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50110, 50107, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50108, 'test-trigger-permissions-trigger-{D}-{R}', '{50111} or {50112}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50111, 50108, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50112, 50108, 50103, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50111, 50108, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50112, 50108, 50103, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50109, 'test-trigger-permissions-trigger-{D}-{W}', '{50113} or {50114}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50113, 50109, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50114, 50109, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50113, 50109, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50114, 50109, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50110, 'test-trigger-permissions-trigger-{R}-{W}', '{50115} or {50116}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50115, 50110, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50116, 50110, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50115, 50110, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50116, 50110, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50111, 'test-trigger-permissions-trigger-{N}-{D}-{R}', '{50117} or {50118} or {50119}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50117, 50111, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50118, 50111, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50119, 50111, 50103, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50117, 50111, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50118, 50111, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50119, 50111, 50103, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50112, 'test-trigger-permissions-trigger-{N}-{D}-{W}', '{50120} or {50121} or {50122}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50120, 50112, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50121, 50112, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50122, 50112, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50120, 50112, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50121, 50112, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50122, 50112, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50113, 'test-trigger-permissions-trigger-{N}-{R}-{W}', '{50123} or {50124} or {50125}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50123, 50113, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50124, 50113, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50125, 50113, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50123, 50113, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50124, 50113, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50125, 50113, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50114, 'test-trigger-permissions-trigger-{D}-{R}-{W}', '{50126} or {50127} or {50128}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50126, 50114, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50127, 50114, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50128, 50114, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50126, 50114, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50127, 50114, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50128, 50114, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50115, 'test-trigger-permissions-trigger-{N}-{D}-{R}-{W}', '{50129} or {50130} or {50131} or {50132}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50129, 50115, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50130, 50115, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50131, 50115, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50132, 50115, 50104, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50129, 50115, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50130, 50115, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50131, 50115, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50132, 50115, 50104, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50116, 'test-trigger-permissions-trigger-{ND}', '{50133}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50133, 50116, 50105, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50133, 50116, 50105, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50117, 'test-trigger-permissions-trigger-{NR}', '{50134}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50134, 50117, 50106, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50134, 50117, 50106, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50118, 'test-trigger-permissions-trigger-{NW}', '{50135}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50135, 50118, 50107, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50135, 50118, 50107, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50119, 'test-trigger-permissions-trigger-{DR}', '{50136}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50136, 50119, 50108, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50136, 50119, 50108, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50120, 'test-trigger-permissions-trigger-{DW}', '{50137}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50137, 50120, 50109, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50137, 50120, 50109, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50121, 'test-trigger-permissions-trigger-{RW}', '{50138}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50138, 50121, 50110, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50138, 50121, 50110, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50122, 'test-trigger-permissions-trigger-{NDR}', '{50139}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50139, 50122, 50111, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50139, 50122, 50111, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50123, 'test-trigger-permissions-trigger-{NDW}', '{50140}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50140, 50123, 50112, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50140, 50123, 50112, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50124, 'test-trigger-permissions-trigger-{NRW}', '{50141}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50141, 50124, 50113, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50141, 50124, 50113, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50125, 'test-trigger-permissions-trigger-{DRW}', '{50142}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50142, 50125, 50114, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50142, 50125, 50114, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50126, 'test-trigger-permissions-trigger-{NDRW}', '{50143}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50143, 50126, 50115, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50143, 50126, 50115, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50127, 'test-trigger-permissions-trigger-{N}-{ND}', '{50144} or {50145}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50144, 50127, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50145, 50127, 50105, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50144, 50127, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50145, 50127, 50105, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50128, 'test-trigger-permissions-trigger-{N}-{NR}', '{50146} or {50147}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50146, 50128, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50147, 50128, 50106, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50146, 50128, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50147, 50128, 50106, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50129, 'test-trigger-permissions-trigger-{N}-{NW}', '{50148} or {50149}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50148, 50129, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50149, 50129, 50107, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50148, 50129, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50149, 50129, 50107, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50130, 'test-trigger-permissions-trigger-{N}-{DR}', '{50150} or {50151}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50150, 50130, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50151, 50130, 50108, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50150, 50130, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50151, 50130, 50108, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50131, 'test-trigger-permissions-trigger-{N}-{DW}', '{50152} or {50153}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50152, 50131, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50153, 50131, 50109, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50152, 50131, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50153, 50131, 50109, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50132, 'test-trigger-permissions-trigger-{N}-{RW}', '{50154} or {50155}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50154, 50132, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50155, 50132, 50110, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50154, 50132, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50155, 50132, 50110, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50133, 'test-trigger-permissions-trigger-{N}-{NDR}', '{50156} or {50157}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50156, 50133, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50157, 50133, 50111, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50156, 50133, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50157, 50133, 50111, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50134, 'test-trigger-permissions-trigger-{N}-{NDW}', '{50158} or {50159}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50158, 50134, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50159, 50134, 50112, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50158, 50134, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50159, 50134, 50112, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50135, 'test-trigger-permissions-trigger-{N}-{NRW}', '{50160} or {50161}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50160, 50135, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50161, 50135, 50113, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50160, 50135, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50161, 50135, 50113, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50136, 'test-trigger-permissions-trigger-{N}-{DRW}', '{50162} or {50163}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50162, 50136, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50163, 50136, 50114, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50162, 50136, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50163, 50136, 50114, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50137, 'test-trigger-permissions-trigger-{N}-{NDRW}', '{50164} or {50165}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50164, 50137, 50101, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50165, 50137, 50115, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50164, 50137, 50101, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50165, 50137, 50115, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50138, 'test-trigger-permissions-trigger-{D}-{ND}', '{50166} or {50167}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50166, 50138, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50167, 50138, 50105, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50166, 50138, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50167, 50138, 50105, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50139, 'test-trigger-permissions-trigger-{D}-{NR}', '{50168} or {50169}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50168, 50139, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50169, 50139, 50106, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50168, 50139, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50169, 50139, 50106, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50140, 'test-trigger-permissions-trigger-{D}-{NW}', '{50170} or {50171}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50170, 50140, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50171, 50140, 50107, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50170, 50140, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50171, 50140, 50107, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50142, 'test-trigger-permissions-trigger-{D}-{DR}', '{50172} or {50173}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50172, 50142, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50173, 50142, 50108, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50172, 50142, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50173, 50142, 50108, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50143, 'test-trigger-permissions-trigger-{D}-{DW}', '{50174} or {50175}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50174, 50143, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50175, 50143, 50109, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50174, 50143, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50175, 50143, 50109, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50144, 'test-trigger-permissions-trigger-{D}-{RW}', '{50176} or {50177}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50176, 50144, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50177, 50144, 50110, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50176, 50144, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50177, 50144, 50110, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50145, 'test-trigger-permissions-trigger-{D}-{NDR}', '{50178} or {50179}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50178, 50145, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50179, 50145, 50111, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50178, 50145, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50179, 50145, 50111, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50146, 'test-trigger-permissions-trigger-{D}-{NDW}', '{50180} or {50181}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50180, 50146, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50181, 50146, 50112, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50180, 50146, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50181, 50146, 50112, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50147, 'test-trigger-permissions-trigger-{D}-{NRW}', '{50182} or {50183}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50182, 50147, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50183, 50147, 50113, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50182, 50147, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50183, 50147, 50113, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50148, 'test-trigger-permissions-trigger-{D}-{DRW}', '{50184} or {50185}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50184, 50148, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50185, 50148, 50114, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50184, 50148, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50185, 50148, 50114, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50149, 'test-trigger-permissions-trigger-{D}-{NDRW}', '{50186} or {50187}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50186, 50149, 50102, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50187, 50149, 50115, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50186, 50149, 50102, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50187, 50149, 50115, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50150, 'test-trigger-permissions-trigger-{R}-{ND}', '{50188} or {50189}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50188, 50150, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50189, 50150, 50105, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50188, 50150, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50189, 50150, 50105, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50151, 'test-trigger-permissions-trigger-{R}-{NR}', '{50190} or {50191}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50190, 50151, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50191, 50151, 50106, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50190, 50151, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50191, 50151, 50106, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50152, 'test-trigger-permissions-trigger-{R}-{NW}', '{50192} or {50193}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50192, 50152, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50193, 50152, 50107, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50192, 50152, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50193, 50152, 50107, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50153, 'test-trigger-permissions-trigger-{R}-{DR}', '{50194} or {50195}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50194, 50153, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50195, 50153, 50108, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50194, 50153, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50195, 50153, 50108, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50154, 'test-trigger-permissions-trigger-{R}-{DW}', '{50196} or {50197}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50196, 50154, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50197, 50154, 50109, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50196, 50154, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50197, 50154, 50109, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50155, 'test-trigger-permissions-trigger-{R}-{RW}', '{50198} or {50199}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50198, 50155, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50199, 50155, 50110, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50198, 50155, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50199, 50155, 50110, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50156, 'test-trigger-permissions-trigger-{R}-{NDR}', '{50200} or {50201}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50200, 50156, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50201, 50156, 50111, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50200, 50156, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50201, 50156, 50111, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50157, 'test-trigger-permissions-trigger-{R}-{NDW}', '{50202} or {50203}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50202, 50157, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50203, 50157, 50112, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50202, 50157, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50203, 50157, 50112, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50158, 'test-trigger-permissions-trigger-{R}-{NRW}', '{50204} or {50205}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50204, 50158, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50205, 50158, 50113, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50204, 50158, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50205, 50158, 50113, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50159, 'test-trigger-permissions-trigger-{R}-{DRW}', '{50206} or {50207}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50206, 50159, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50207, 50159, 50114, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50206, 50159, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50207, 50159, 50114, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50160, 'test-trigger-permissions-trigger-{R}-{NDRW}', '{50208} or {50209}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50208, 50160, 50103, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50209, 50160, 50115, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50208, 50160, 50103, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50209, 50160, 50115, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50161, 'test-trigger-permissions-trigger-{W}-{ND}', '{50210} or {50211}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50210, 50161, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50211, 50161, 50105, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50210, 50161, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50211, 50161, 50105, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50162, 'test-trigger-permissions-trigger-{W}-{NR}', '{50212} or {50213}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50212, 50162, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50213, 50162, 50106, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50212, 50162, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50213, 50162, 50106, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50163, 'test-trigger-permissions-trigger-{W}-{NW}', '{50214} or {50215}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50214, 50163, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50215, 50163, 50107, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50214, 50163, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50215, 50163, 50107, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50164, 'test-trigger-permissions-trigger-{W}-{DR}', '{50216} or {50217}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50216, 50164, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50217, 50164, 50108, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50216, 50164, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50217, 50164, 50108, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50165, 'test-trigger-permissions-trigger-{W}-{DW}', '{50218} or {50219}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50218, 50165, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50219, 50165, 50109, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50218, 50165, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50219, 50165, 50109, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50166, 'test-trigger-permissions-trigger-{W}-{RW}', '{50220} or {50221}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50220, 50166, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50221, 50166, 50110, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50220, 50166, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50221, 50166, 50110, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50167, 'test-trigger-permissions-trigger-{W}-{NDR}', '{50222} or {50223}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50222, 50167, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50223, 50167, 50111, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50222, 50167, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50223, 50167, 50111, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50168, 'test-trigger-permissions-trigger-{W}-{NDW}', '{50224} or {50225}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50224, 50168, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50225, 50168, 50112, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50224, 50168, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50225, 50168, 50112, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50169, 'test-trigger-permissions-trigger-{W}-{NRW}', '{50226} or {50227}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50226, 50169, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50227, 50169, 50113, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50226, 50169, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50227, 50169, 50113, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50170, 'test-trigger-permissions-trigger-{W}-{DRW}', '{50228} or {50229}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50228, 50170, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50229, 50170, 50114, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50228, 50170, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50229, 50170, 50114, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50171, 'test-trigger-permissions-trigger-{W}-{NDRW}', '{50230} or {50231}', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50230, 50171, 50104, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50231, 50171, 50115, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50230, 50171, 50104, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50231, 50171, 50115, 'last', '$');
-- trigger permissions: END
@@ -1562,20 +1562,20 @@ INSERT INTO host_tag (hosttagid, hostid, tag, value) VALUES (1013, 99025, 'Webbr
INSERT INTO host_tag (hosttagid, hostid, tag, value) VALUES (1014, 99027, 'office', 'Riga');
INSERT INTO items (itemid, hostid, interfaceid, type, value_type, name, key_, delay, history, status, params, description, posts, headers) VALUES (58736, 99013, NULL, 2, 3, 'Item', 'item', 0, 90, 0, '', '', '', '');
INSERT INTO triggers (triggerid, description, expression, comments, value) VALUES (50172, 'trigger1', '{50232}=1', '', '1');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50232, 50172, 58736, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50232, 50172, 58736, 'last', '$');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1001, 50172, 'tag1', 'value1');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1002, 50172, 'tag2', '');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1003, 50172, 'tag3', 'value3');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1004, 50172, 'tag3', 'value4');
INSERT INTO triggers (triggerid, description, expression, comments, value) VALUES (50173, 'trigger2', '{50233}=1', '', '1');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50233, 50173, 58736, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50233, 50173, 58736, 'last', '$');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1005, 50173, 'tag1', 'value5');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1006, 50173, 'tag2', 'value6');
INSERT INTO triggers (triggerid, description, expression, comments, value) VALUES (50174, 'trigger3', '{50234}=1', '', '1');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50234, 50174, 58736, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50234, 50174, 58736, 'last', '$');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (1007, 50174, 'tag1', 'value7');
INSERT INTO triggers (triggerid, description, expression, comments, value) VALUES (50175, 'trigger4', '{50235}=1', '', '1');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50235, 50175, 58736, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50235, 50175, 58736, 'last', '$');
INSERT INTO events (eventid, source, object, objectid, clock, value, acknowledged, ns, name, severity) VALUES (5000, 0, 0, 50172, 1610000000, 1, 0, 0, 'trigger1', 0);
INSERT INTO event_tag (eventtagid, eventid, tag, value) VALUES (1000, 5000, 'tag1', 'value1');
INSERT INTO event_tag (eventtagid, eventid, tag, value) VALUES (1001, 5000, 'tag2', '');
@@ -1624,8 +1624,8 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (50039, 99029, 50
INSERT INTO items (itemid, hostid, interfaceid, type, value_type, name, key_, delay, history, status, params, description, posts, headers) VALUES (58737, 99028, NULL, 2, 3, 'item', 'item', '1d', '90d', 0, '', '', '', '');
INSERT INTO items (itemid, hostid, interfaceid, type, value_type, name, key_, delay, history, status, params, description, posts, headers) VALUES (58738, 99029, NULL, 2, 3, 'item', 'item', '1d', '90d', 0, '', '', '', '');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50176, 'test-trigger-1', '{50236}=0', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50236, 50176, 58737, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50236, 50176, 58737, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50177, 'test-trigger-2', '{50237}=0', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50237, 50177, 58737, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50237, 50177, 58737, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, comments) VALUES (50178, 'template-trigger', '{50238}=0', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50238, 50178, 58738, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (50238, 50178, 58738, 'last', '$');
diff --git a/ui/tests/api_json/testConfiguration.php b/ui/tests/api_json/testConfiguration.php
index 00e290a0209..4330af869d4 100644
--- a/ui/tests/api_json/testConfiguration.php
+++ b/ui/tests/api_json/testConfiguration.php
@@ -145,8 +145,7 @@ class testConfiguration extends CAPITest {
['hosts'],
['images'],
['maps'],
- ['templates'],
- ['valueMaps']
+ ['templates']
];
}
diff --git a/ui/tests/api_json/testTriggerValidation.php b/ui/tests/api_json/testTriggerValidation.php
index cca6c9409bf..a5bde6dac4d 100644
--- a/ui/tests/api_json/testTriggerValidation.php
+++ b/ui/tests/api_json/testTriggerValidation.php
@@ -163,11 +163,11 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with unique name 1',
- 'expression' => '{Trigger validation test host:item.last()}=0'
+ 'expression' => 'last(/Trigger validation test host/item)=0'
],
[
'description' => 'Trigger with unique name 2',
- 'expression' => '{Trigger validation test host:item.last()}=0'
+ 'expression' => 'last(/Trigger validation test host/item)=0'
]
],
'expected_error' => null
@@ -176,7 +176,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with null values for array properties',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [],
'dependencies' => []
]
@@ -187,9 +187,9 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with recovery expression',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 1,
- 'recovery_expression' => '{Trigger validation test host:item.last()}=1'
+ 'recovery_expression' => 'last(/Trigger validation test host/item)=1'
]
],
'expected_error' => null
@@ -198,7 +198,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with correlation tag',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 0,
'correlation_mode' => 1,
'correlation_tag' => 'tag'
@@ -210,7 +210,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [
[
'tag' => 'tag1'
@@ -227,7 +227,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with dependencies',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => [
[
'triggerid' => self::UPDATE_TRIGGER_1
@@ -243,20 +243,20 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Duplicate trigger name',
- 'expression' => '{Trigger validation test host:item.last()}=0'
+ 'expression' => 'last(/Trigger validation test host/item)=0'
],
[
'description' => 'Duplicate trigger name',
- 'expression' => '{Trigger validation test host:item.last()}=0'
+ 'expression' => 'last(/Trigger validation test host/item)=0'
]
],
- 'expected_error' => 'Invalid parameter "/2": value (description, expression)=(Duplicate trigger name, {Trigger validation test host:item.last()}=0) already exists.'
+ 'expected_error' => 'Invalid parameter "/2": value (description, expression)=(Duplicate trigger name, last(/Trigger validation test host/item)=0) already exists.'
],
'Trigger with invalid severity #1' => [
'triggers' => [
[
'description' => 'Trigger with invalid severity',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'priority' => null
]
],
@@ -266,7 +266,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid severity',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'priority' => 9
]
],
@@ -276,8 +276,8 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with unexpected recovery exporession',
- 'expression' => '{Trigger validation test host:item.last()}=0',
- 'recovery_expression' => '{Trigger validation test host:item.last()}=1'
+ 'expression' => 'last(/Trigger validation test host/item)=0',
+ 'recovery_expression' => 'last(/Trigger validation test host/item)=1'
]
],
'expected_error' => 'Incorrect value for field "recovery_expression": should be empty.'
@@ -286,7 +286,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with unspecified recovery exporession',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 1
]
],
@@ -296,9 +296,9 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with expected recovery exporession',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 1,
- 'recovery_expression' => ['{Trigger validation test host:item.last()}=1']
+ 'recovery_expression' => ['last(/Trigger validation test host/item)=1']
]
],
'expected_error' => 'Invalid parameter "/1/recovery_expression": a character string is expected.'
@@ -307,18 +307,18 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with expected recovery exporession',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 1,
'recovery_expression' => '1+1'
]
],
- 'expected_error' => 'Invalid parameter "/1/recovery_expression": trigger expression must contain at least one host:key reference.'
+ 'expected_error' => 'Invalid parameter "/1/recovery_expression": trigger expression must contain at least one /host/key reference.'
],
'Trigger with unexpected correlation tag #1' => [
'triggers' => [
[
'description' => 'Trigger with unexpected correlation tag',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'correlation_mode' => 0,
'correlation_tag' => 'tag'
]
@@ -329,7 +329,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with unexpected correlation tag',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'recovery_mode' => 2,
'correlation_tag' => 'tag'
]
@@ -340,7 +340,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with unexpected correlation tag',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'correlation_mode' => 1,
'correlation_tag' => ''
]
@@ -351,7 +351,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [[]]
]
],
@@ -361,7 +361,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [[
'tag' => '',
'value' => 'value'
@@ -374,7 +374,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid dependencies',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => [[
'triggerid' => ''
]]
@@ -386,7 +386,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid dependencies',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => [[
'triggerid' => self::TEMPLATE_TRIGGERID
]]
@@ -398,7 +398,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with invalid dependencies',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => [[
'triggerid' => 0
]]
@@ -410,7 +410,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with non-unique tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [
[
'tag' => 'tag'
@@ -427,7 +427,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with non-unique tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [
[
'tag' => 'tag',
@@ -446,7 +446,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with null values for array properties',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => null
]
],
@@ -456,7 +456,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with null values for array properties',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => null
]
],
@@ -466,7 +466,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with tags',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'tags' => [
'tag' => 'tag1',
'tag' => 'tag2'
@@ -479,7 +479,7 @@ class testTriggerValidation extends CAPITest {
'triggers' => [
[
'description' => 'Trigger with dependencies',
- 'expression' => '{Trigger validation test host:item.last()}=0',
+ 'expression' => 'last(/Trigger validation test host/item)=0',
'dependencies' => [
'triggerid' => self::UPDATE_TRIGGER_1
]
diff --git a/ui/tests/bootstrap.php.template b/ui/tests/bootstrap.php.template
index 5ee1a8d5d5d..468b34b683b 100644
--- a/ui/tests/bootstrap.php.template
+++ b/ui/tests/bootstrap.php.template
@@ -26,6 +26,9 @@ define('PHPUNIT_CONFIG_DIR', '{CONFIG_DIR}');
define('PHPUNIT_COMPONENT_DIR', '{COMPONENT_DIR}');
define('PHPUNIT_PORT_PREFIX', '{PORT_PREFIX}');
+define('PHPUNIT_DATA_DIR', '{DATA_DIR}');
+define('PHPUNIT_DATA_SOURCES_DIR', '{DATA_SOURCES_DIR}');
+
// SAML settings.
define('PHPUNIT_SAML_TESTS_ENABLED', false);
define('PHPUNIT_IDP_ENTITY_ID', '{PHPUNIT_IDP_ENTITY_ID}');
diff --git a/ui/tests/include/CTest.php b/ui/tests/include/CTest.php
index 021798b0ee9..78b77aaafd2 100644
--- a/ui/tests/include/CTest.php
+++ b/ui/tests/include/CTest.php
@@ -25,6 +25,7 @@ require_once dirname(__FILE__).'/../../include/hosts.inc.php';
require_once dirname(__FILE__).'/helpers/CDBHelper.php';
require_once dirname(__FILE__).'/helpers/CAPIHelper.php';
+require_once dirname(__FILE__).'/helpers/CDataHelper.php';
require_once dirname(__FILE__).'/helpers/CExceptionHelper.php';
require_once dirname(__FILE__).'/helpers/CTestArrayHelper.php';
require_once dirname(__FILE__).'/helpers/CDateTimeHelper.php';
@@ -200,6 +201,12 @@ class CTest extends PHPUnit_Framework_TestCase {
// Test suite level annotations.
$class_annotations = $this->getAnnotationsByType($this->annotations, 'class');
+ // Data sources are processed before the backups.
+ $data_source = $this->getAnnotationTokensByName($class_annotations, 'dataSource');
+ if ($data_source) {
+ CDataHelper::load($data_source);
+ }
+
// Backup performed before test suite execution.
$suite_backup = $this->getAnnotationTokensByName($class_annotations, 'backup');
@@ -274,6 +281,12 @@ class CTest extends PHPUnit_Framework_TestCase {
$method_annotations = $this->getAnnotationsByType($this->annotations, 'method');
if ($method_annotations !== null) {
+ // Data sources are processed before the backups.
+ $data_source = $this->getAnnotationTokensByName($method_annotations, 'dataSource');
+ if ($data_source) {
+ CDataHelper::load($data_source);
+ }
+
// Backup performed before every test case execution.
$case_backup = $this->getAnnotationTokensByName($method_annotations, 'backup');
diff --git a/ui/tests/include/helpers/CDataHelper.php b/ui/tests/include/helpers/CDataHelper.php
index 764ab0c9c81..6814d576012 100644
--- a/ui/tests/include/helpers/CDataHelper.php
+++ b/ui/tests/include/helpers/CDataHelper.php
@@ -26,6 +26,7 @@ require_once dirname(__FILE__).'/../../../include/hosts.inc.php';
class CDataHelper extends CAPIHelper {
+ protected static $data = null;
protected static $request = [];
protected static $response = [];
@@ -216,4 +217,86 @@ class CDataHelper extends CAPIHelper {
return $result;
}
+
+ /**
+ * Load the data source data from the file cache.
+ */
+ protected static function preload() {
+ if (static::$data === null) {
+ static::$data = [];
+
+ if (!defined('PHPUNIT_DATA_DIR')) {
+ return;
+ }
+
+ foreach (new DirectoryIterator(PHPUNIT_DATA_DIR) as $file) {
+ if ($file->isDot() || $file->isDir() || strtolower($file->getExtension()) !== 'json') {
+ continue;
+ }
+
+ $name = $file->getBasename('.'.$file->getExtension());
+ static::$data[$name] = json_decode(file_get_contents($file->getPathname()), true);
+ }
+ }
+ }
+
+ /**
+ * Get data from the data sources.
+ *
+ * @param mixed $path data path to look for
+ * @param mixed $default default value to be returned if data doesn't exist
+ *
+ * @return mixed
+ */
+ public static function get($path, $default = null) {
+ return CTestArrayHelper::get(static::$data, $path, $default);
+ }
+
+ /**
+ * Load specific data source data.
+ *
+ * @param mixed $source name of the data source(s)
+ *
+ * @return boolean
+ *
+ * @throws \Exception
+ */
+ public static function load($source) {
+ if (is_array($source)) {
+ $result = true;
+ foreach ($source as $name) {
+ $result &= static::load($name);
+ }
+
+ return $result;
+ }
+
+ static::preload();
+
+ if (array_key_exists($source, static::$data)) {
+ return true;
+ }
+
+ try {
+ $path = PHPUNIT_DATA_SOURCES_DIR.$source.'.php';
+ if (!file_exists($path)) {
+ throw new \Exception('File "'.$path.'" doesn\'t exist.');
+ }
+
+ require_once $path;
+ static::$data[$source] = forward_static_call([$source, 'load']);
+
+ if (defined('PHPUNIT_DATA_DIR')) {
+ $data = json_encode(static::get($source));
+ file_put_contents(PHPUNIT_DATA_DIR.$source.'.json', $data);
+ }
+ }
+ catch (\Exception $e) {
+ echo 'Failed to load data from data source "'.$source.'".'."\n\n".$e->getMessage()."\n".$e->getTraceAsString();
+
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/ui/tests/selenium/common/testItemTest.php b/ui/tests/selenium/common/testItemTest.php
index 3b2c508951d..70b9db548a7 100644
--- a/ui/tests/selenium/common/testItemTest.php
+++ b/ui/tests/selenium/common/testItemTest.php
@@ -70,7 +70,6 @@ class testItemTest extends CWebTest {
public function getItemTestButtonStateData() {
return array_merge($this->getCommonTestButtonStateData(), [
['Type' => 'SNMP trap', 'Key' => 'snmptrap.fallback'],
- ['Type' => 'Zabbix aggregate', 'Key' => 'grpmax["Zabbix",key,last]'],
['Type' => 'Calculated', 'Formula' => '"formula"']
]);
}
@@ -522,25 +521,6 @@ class testItemTest extends CWebTest {
[
'expected' => TEST_GOOD,
'fields' => [
- 'Type' => 'Zabbix aggregate',
- 'Key' => 'grpsum["MySQL Servers","vfs.fs.size[/,total]",last]'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'fields' => [
- 'Type' => 'Zabbix aggregate',
- 'Key' => 'key'
- ],
- 'error' => 'Key "key" does not match <grpmax|grpmin|grpsum|grpavg>["Host group(s)", "Item key", "<last|min|max|avg|sum|count>", "parameter"].'
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'fields' => [
'Type' => 'Calculated',
'Key' => 'test.calculated'
]
@@ -873,7 +853,6 @@ class testItemTest extends CWebTest {
$fields_state = ['address' => false, 'port' => false, 'proxy' => true];
break;
- case 'Zabbix aggregate':
case 'Calculated':
$fields_value = ['address' => '', 'port' => '', 'proxy' => '(no proxy)'];
$fields_state = ['address' => false, 'port' => false, 'proxy' => false];
@@ -898,7 +877,7 @@ class testItemTest extends CWebTest {
if ($is_host || array_key_exists('interface', $data) || in_array($data['fields']['Type'],
['Zabbix internal', 'External check', 'Database monitor', 'HTTP agent', 'JMX agent',
- 'Zabbix aggregate', 'Calculated'])) {
+ 'Calculated'])) {
$details = 'Connection to Zabbix server "localhost" refused. Possible reasons:';
}
else {
diff --git a/ui/tests/selenium/data/data_test.sql b/ui/tests/selenium/data/data_test.sql
index e9372e6986b..c9878288c7a 100644
--- a/ui/tests/selenium/data/data_test.sql
+++ b/ui/tests/selenium/data/data_test.sql
@@ -618,7 +618,7 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (90279, 20006, 4)
INSERT INTO interface (type, ip, dns, useip, port, main, hostid, interfaceid) VALUES (1, '127.0.0.1', '', '1', '10050', '1', 20006, 10025);
INSERT INTO items (itemid, name, key_, hostid, interfaceid, delay, value_type, params, description, posts, headers) VALUES (24338, 'item1', 'key1', 20006, 10025, '30s', 3, '', '', '', '');
INSERT INTO triggers (triggerid, description, value, state, lastchange, comments) VALUES (100029, 'trigger host.host:{HOST.HOST} | host.host2:{HOST.HOST2} | host.name:{HOST.NAME} | item.value:{ITEM.VALUE} | item.value1:{ITEM.VALUE1} | item.lastvalue:{ITEM.LASTVALUE} | host.ip:{HOST.IP} | host.dns:{HOST.DNS} | host.conn:{HOST.CONN}', 0, 1, '1339761311', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99946, 24338, 100029, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99946, 24338, 100029, 'last', '$,#1');
-- inheritance testing
INSERT INTO hosts (hostid, host, name, status, description) VALUES (15000, 'Inheritance test template', 'Inheritance test template', 3, '');
@@ -718,14 +718,14 @@ INSERT INTO triggers (triggerid, expression, description, comments, templateid)
INSERT INTO triggers (triggerid, expression, description, comments, templateid) VALUES (99005, '{99734}=0', 'testInheritanceTrigger2', '', 99001);
INSERT INTO triggers (triggerid, expression, description, comments, templateid) VALUES (99006, '{99735}=0', 'testInheritanceTrigger3', '', 99002);
INSERT INTO triggers (triggerid, expression, description, comments, templateid) VALUES (99007, '{99736}=0', 'testInheritanceTrigger4', '', 99003);
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99729, 99000, 15000, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99730, 99001, 15000, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99731, 99002, 15000, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99732, 99003, 15000, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99733, 99004, 15005, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99734, 99005, 15005, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99735, 99006, 15005, 'last', '');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99736, 99007, 15005, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99729, 99000, 15000, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99730, 99001, 15000, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99731, 99002, 15000, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99732, 99003, 15000, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99733, 99004, 15005, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99734, 99005, 15005, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99735, 99006, 15005, 'last', '$');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (99736, 99007, 15005, 'last', '$');
-- testFormGraph.LayoutCheck testInheritanceGraph.SimpleUpdate
INSERT INTO graphs (graphid, name) VALUES (15000, 'testInheritanceGraph1');
@@ -855,14 +855,14 @@ INSERT INTO triggers (triggerid, expression, description, comments, flags, templ
INSERT INTO triggers (triggerid, expression, description, comments, flags, templateid) VALUES (99013, '{99742}=0', 'testInheritanceTriggerPrototype2', '', 2, 99009);
INSERT INTO triggers (triggerid, expression, description, comments, flags, templateid) VALUES (99014, '{99743}=0', 'testInheritanceTriggerPrototype3', '', 2, 99010);
INSERT INTO triggers (triggerid, expression, description, comments, flags, templateid) VALUES (99015, '{99744}=0', 'testInheritanceTriggerPrototype4', '', 2, 99011);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99737, 15021, 99008, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99738, 15021, 99009, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99739, 15021, 99010, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99740, 15021, 99011, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99741, 15026, 99012, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99742, 15026, 99013, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99743, 15026, 99014, 'last', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99744, 15026, 99015, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99737, 15021, 99008, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99738, 15021, 99009, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99739, 15021, 99010, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99740, 15021, 99011, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99741, 15026, 99012, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99742, 15026, 99013, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99743, 15026, 99014, 'last', '$');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (99744, 15026, 99015, 'last', '$');
-- testInheritanceWeb.SimpleUpdate
INSERT INTO httptest (httptestid, name, delay, agent, hostid) VALUES (15000, 'testInheritanceWeb1', '1m', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', 15000);
@@ -1015,16 +1015,16 @@ INSERT INTO items (itemid, type, hostid, name, description, key_, delay, history
-- testFormTrigger.SimpleUpdate
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (14000, '{14000}=0', 'testFormTrigger1', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14000, 99102, 14000, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14000, 99102, 14000, 'last', '$,#1');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (14001, '{14001}=0', 'testFormTrigger2', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14001, 99102, 14001, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14001, 99102, 14001, 'last', '$,#1');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (14002, '{14002}=0', 'testFormTrigger3', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14002, 99102, 14002, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14002, 99102, 14002, 'last', '$,#1');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (14003, '{14003}=0', 'testFormTrigger4', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14003, 99102, 14003, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (14003, 99102, 14003, 'last', '$,#1');
-- testFormGraph.LayoutCheck testFormGraph.SimpleUpdate
INSERT INTO graphs (graphid, name, width, height, yaxismin, yaxismax, templateid, show_work_period, show_triggers, graphtype, show_legend, show_3d, percent_left, percent_right, ymin_type, ymax_type, ymin_itemid, ymax_itemid, flags) VALUES (300000,'testFormGraph1',900,200,0.0,100.0,NULL,1,0,1,1,0,0.0,0.0,1,1,NULL,NULL,0);
@@ -1068,12 +1068,12 @@ INSERT INTO triggers (triggerid,expression,description,url,status,value,priority
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (99521,'{99950}=0','testFormTriggerPrototype4','',0,0,0,0,'','',NULL,0,0,2);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (99522,'{99951}=0','Trigger prototype with tags for updating','',0,0,0,0,'','',NULL,0,0,2);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (99523,'{99952}=0','Trigger prototype with tags for cloning','',0,0,0,0,'','',NULL,0,0,2);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99947,23804,99518,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99948,23804,99519,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99949,23804,99520,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99950,23804,99521,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99951,23804,99522,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99952,23804,99523,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99947,23804,99518,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99948,23804,99519,'last','$,#2');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99949,23804,99520,'last','$,#4');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99950,23804,99521,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99951,23804,99522,'last','$,#2');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (99952,23804,99523,'last','$,#3');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (200, 99522, 'action', 'update');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (201, 99522, 'tag', 'trigger_prototype');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (202, 99523, 'action', 'clode');
@@ -1223,16 +1223,16 @@ INSERT INTO triggers (triggerid,expression,description,url,status,value,priority
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100015,'{100015}=0','TriggerProto ZBX6663 Second','',0,0,0,0,'','',100014,0,0,2);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100016,'{100016}=0','TriggerProto ZBX6663 Second','',0,0,0,0,'','',100014,0,0,2);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100017,'{100017}=0','TriggerProto ZBX6663 HSecond','',0,0,0,0,'','',NULL,0,0,2);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100008,40038,100008,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100009,40039,100009,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100010,40040,100010,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100011,40041,100011,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100012,40042,100012,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100013,40054,100013,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100014,40048,100014,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100015,40049,100015,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100016,40050,100016,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100017,40052,100017,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100008,40038,100008,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100009,40039,100009,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100010,40040,100010,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100011,40041,100011,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100012,40042,100012,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100013,40054,100013,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100014,40048,100014,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100015,40049,100015,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100016,40050,100016,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100017,40052,100017,'last','$,#1');
INSERT INTO graphs (graphid,name,width,height,yaxismin,yaxismax,templateid,show_work_period,show_triggers,graphtype,show_legend,show_3d,percent_left,percent_right,ymin_type,ymax_type,ymin_itemid,ymax_itemid,flags) VALUES (700008,'Graph ZBX6663',900,200,0.0000,100.0000,NULL,1,1,0,1,0,0.0000,0.0000,0,0,NULL,NULL,0);
INSERT INTO graphs (graphid,name,width,height,yaxismin,yaxismax,templateid,show_work_period,show_triggers,graphtype,show_legend,show_3d,percent_left,percent_right,ymin_type,ymax_type,ymin_itemid,ymax_itemid,flags) VALUES (700009,'Graph ZBX6663 Second',900,200,0.0000,100.0000,NULL,1,1,0,1,0,0.0000,0.0000,0,0,NULL,NULL,0);
INSERT INTO graphs (graphid,name,width,height,yaxismin,yaxismax,templateid,show_work_period,show_triggers,graphtype,show_legend,show_3d,percent_left,percent_right,ymin_type,ymax_type,ymin_itemid,ymax_itemid,flags) VALUES (700010,'Graph ZBX6663 Second',900,200,0.0000,100.0000,700009,1,1,0,1,0,0.0000,0.0000,0,0,NULL,NULL,0);
@@ -1300,10 +1300,10 @@ INSERT INTO triggers (triggerid,expression,description,url,status,value,priority
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100019,'{100019}=0','zbx6648 trigger enabled','',0,0,0,0,'','',NULL,0,0,0);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100020,'{100020}=0','zbx6648 trigger all enabled','',0,0,0,0,'','',NULL,0,0,0);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100021,'{100021}=0','zbx6648 trigger all disabled','',1,0,0,0,'','',NULL,0,0,0);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100018,40055,100018,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100019,40056,100019,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100020,40057,100020,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100021,40057,100021,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100018,40055,100018,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100019,40056,100019,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100020,40057,100020,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100021,40057,100021,'last','$,#1');
-- testPageItems, testPageTriggers, testPageDiscoveryRules, testPageItemPrototype, testPageTriggerPrototype
INSERT INTO hosts (hostid, host, name, status, description) VALUES (50006, 'Template-layout-test-001', 'Template-layout-test-001', 3, '');
@@ -1321,13 +1321,13 @@ INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (514,
INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (40062,0,50006,'Item-layout-test-001','item-layout-test-001','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50020,'',0,'30','','');
INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (40063,0,50007,'Item-layout-test-002','item-layout-test-002','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50019,'{{$A}}',0,'30','','');
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100022,'{100022}=0','Trigger-proto-layout-test-001','',0,0,0,0,'','',NULL,0,0,2);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100022,40060,100022,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100022,40060,100022,'last','$,#1');
INSERT INTO triggers (triggerid, expression, description, comments, flags) VALUES (100023, '{100023}=0', 'Trigger-proto-layout-test-001', '', 2);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100023, 40061 ,100023,'last',0);
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100023, 40061 ,100023,'last','$,#1');
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100024,'{100024}=0','Trigger-layout-test-001','',1,0,0,0,'','',NULL,0,0,0);
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100025,'{100025}=0','Trigger-layout-test-002','',0,0,0,0,'','',NULL,0,0,0);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100024,40063,100024,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100025,40062,100025,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100024,40063,100024,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100025,40062,100025,'last','$,#1');
-- testFormMap.ZBX6840
INSERT INTO hosts (hostid, host, name, status, description) VALUES (50008, 'Host-map-test-zbx6840', 'Host-map-test-zbx6840', 0, '');
@@ -1335,8 +1335,8 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (50008, 50008, 4)
INSERT INTO interface (type, ip, dns, useip, port, main, hostid, interfaceid) VALUES (1, '127.0.7.1', '', '1', '10071', '1', 50008, 50021);
INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (40065,0,50008,'Item-layout-test-zbx6840','item-layout-test-002','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50021,'',0,'30','','');
INSERT INTO triggers (triggerid,expression,description,url,status,value,priority,lastchange,comments,error,templateid,type,state,flags) VALUES (100026,'{100026}=0 and {100027}=0','Trigger-map-test-zbx6840','',0,0,0,0,'','',NULL,0,0,0);
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100026,40065,100026,'last','0');
-INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100027,23287,100026,'last','0');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100026,40065,100026,'last','$,#1');
+INSERT INTO functions (functionid,itemid,triggerid,name,parameter) VALUES (100027,23287,100026,'last','$,#1');
INSERT INTO sysmaps (sysmapid, name, width, height, backgroundid, label_type, label_location, highlight, expandproblem, markelements, show_unack, grid_size, grid_show, grid_align, label_format, label_type_host, label_type_hostgroup, label_type_trigger, label_type_map, label_type_image, label_string_host, label_string_hostgroup, label_string_trigger, label_string_map, label_string_image, iconmapid, expand_macros, severity_min, userid, private) VALUES (5, 'testZBX6840', 800, 600, NULL, 0, 0, 0, 0, 0, 0, 50, 1, 1, 0, 2, 2, 2, 2, 2, '', '', '', '', '', NULL, 0, 0, 1, 0);
INSERT INTO sysmaps_elements (selementid,sysmapid,elementid,elementtype,iconid_off,iconid_on,label,label_location,x,y,iconid_disabled,iconid_maintenance,elementsubtype,areatype,width,height,viewtype,use_iconmap) VALUES (8,5,10084,0,19,NULL,'Host element (Zabbix Server)',-1,413,268,NULL,NULL,0,0,200,200,0,0);
INSERT INTO sysmaps_elements (selementid,sysmapid,elementid,elementtype,iconid_off,iconid_on,label,label_location,x,y,iconid_disabled,iconid_maintenance,elementsubtype,areatype,width,height,viewtype,use_iconmap) VALUES (9,5,0,2,15,NULL,'Trigger element (zbx6840)',-1,213,218,NULL,NULL,0,0,200,200,0,0);
@@ -1438,7 +1438,7 @@ INSERT INTO icon_mapping (iconmappingid, iconmapid, iconid, inventory_link, expr
-- Create two triggers with event
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('Test trigger to check tag filter on problem page','{100185}>100','0','0','','3','','1','0','0','','','99250');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100185','99250','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100185','99250','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Service','abc','99250','97');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('service','abcdef','99250','98');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Database','','99250','99');
@@ -1448,7 +1448,7 @@ INSERT INTO problem (eventid,source,object,objectid,clock,ns,name,severity) VALU
INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (90,92,'Service','abc'),(91,92,'service','abcdef'),(92,92,'Database',''),(98,92,'Tag4',''),(99,92,'Tag5','5');
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('Test trigger with tag','{100186}>100','0','0','','2','','1','0','0','','','99251');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100186','99251','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100186','99251','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Service','abc','99251','100');
INSERT INTO events (eventid,source,object,objectid,clock,ns,value,name,severity) VALUES (93,0,0,99251,1603466628,128786843,1,'Test trigger with tag',2);
INSERT INTO event_tag (eventtagid,eventid,tag,value) VALUES (93,93,'Service','abc');
@@ -1468,10 +1468,10 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (90280, 50009, 50
INSERT INTO interface (type, ip, dns, useip, port, main, hostid, interfaceid) VALUES (1, '127.0.0.1', '', '1', '10050', '1', 50009, 50022);
INSERT INTO items (itemid, name, key_, hostid, interfaceid, delay, value_type, params, description, posts, headers) VALUES (40066, 'tag.item', 'tag.key', 50009, 50022, '30s', 3, '', '', '', '');
INSERT INTO triggers (triggerid, description, expression, value, state, lastchange, comments) VALUES (100027, 'Trigger for tag permissions MySQL', '{13083}=0', 0, 1, '1339761311', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100028, 40066, 100027, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100028, 40066, 100027, 'last', '$,#1');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (101, 'Service','MySQL', 100027);
INSERT INTO triggers (triggerid, description, expression, value, state, lastchange, comments) VALUES (100028, 'Trigger for tag permissions Oracle', '{13083}=0', 0, 1, '1339761311', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100029, 40066, 100028, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100029, 40066, 100028, 'last', '$,#1');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (102, 'Service','Oracle', 100028);
-- Tag based permissions: triggers problems events
INSERT INTO events (eventid,source,object,objectid,clock,ns,value,name) VALUES (94,0,0,100027,1603456528,128786843,1,'Trigger for tag permissions MySQL');
@@ -1728,7 +1728,7 @@ INSERT INTO valuemap_mapping (valuemap_mappingid, valuemapid, value, newvalue) V
-- testPageProblems_TagPriority
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('First test trigger with tag priority','{100181}>100','0','1','','2','','1','0','0','','','99252');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100181','99252','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100181','99252','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Delta','d','99252','105');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Beta','b','99252','106');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Alpha','a','99252','107');
@@ -1745,7 +1745,7 @@ INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (102,96,'Alpha',
INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (103,96,'Gamma','g');
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('Second test trigger with tag priority','{100182}>100','0','1','','2','','1','0','0','','','99253');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100182','99253','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100182','99253','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Zeta','z','99253','109');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Beta','b','99253','110');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Epsilon','e','99253','111');
@@ -1762,7 +1762,7 @@ INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (106,97,'Epsilon
INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (107,97,'Eta','e');
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('Third test trigger with tag priority','{100183}>100','0','1','','2','','1','0','0','','','99254');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100183','99254','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100183','99254','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Kappa','k','99254','113');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Iota','i','99254','114');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Alpha','a','99254','115');
@@ -1779,7 +1779,7 @@ INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (110,98,'Alpha',
INSERT INTO problem_tag (problemtagid,eventid,tag,value) VALUES (111,98,'Theta','t');
INSERT INTO triggers (description,expression,recovery_mode,type,url,priority,comments,manual_close,status,correlation_mode,recovery_expression,correlation_tag,triggerid) VALUES ('Fourth test trigger with tag priority','{100184}>100','0','1','','2','','1','0','0','','','99255');
-INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100184','99255','29192','avg','5m');
+INSERT INTO functions (functionid,triggerid,itemid,name,parameter) VALUES ('100184','99255','29192','avg','$,5m');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Eta','e','99255','117');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Gamma','g','99255','118');
INSERT INTO trigger_tag (tag,value,triggerid,triggertagid) VALUES ('Theta','t','99255','119');
@@ -1802,7 +1802,7 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (99007, 99011, 50
INSERT INTO interface (interfaceid, hostid, main, type, useip, ip, dns, port) values (50025,99011,1,1,1,'127.0.0.1','','10050');
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers) VALUES (99087, 2, 99011, 'Trapper_for_suppression', '', 'trapper_sup', 30, NULL, '', '', '', '', '','');
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100031, 'Trigger_for_suppression', '{100031}>0', 1, 3, 0, '1535012391', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100031, 99087, 100031, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100031, 99087, 100031, 'last', '$,#1');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (104, 'SupTag','A', 100031);
INSERT INTO maintenances (maintenanceid, name, maintenance_type, description, active_since, active_till,tags_evaltype) VALUES (4,'Maintenance for suppression test',0,'',1534971600,2147378400,2);
@@ -1916,35 +1916,35 @@ INSERT INTO interface (interfaceid, hostid, main, type, useip, ip, dns, port) va
INSERT INTO items (itemid, type, hostid, name, description, key_, delay, interfaceid, params, formula, url, posts, query_fields, headers) VALUES (99090, 2, 99050, 'Trapper', '', 'trap', 30, NULL, '', '', '', '', '','');
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100060, 'First trigger for tag filtering', '{100060}>0', 0, 1, 0, '0', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100060, 99090, 100060, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100060, 99090, 100060, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (130, 'TagA','A', 100060);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (131, 'TagB','b', 100060);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (132, 'TagD','d', 100060);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (133, 'TagG','g', 100060);
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100061, 'Second trigger for tag filtering', '{100061}>0', 0, 2, 0, '0', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100061, 99090, 100061, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100061, 99090, 100061, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (134, 'TagB','b', 100061);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (135, 'TagE','e', 100061);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (136, 'TagE1','e', 100061);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (137, 'TagZ','z', 100061);
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100062, 'Third trigger for tag filtering', '{100062}>0', 0, 3, 0, '0', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100062, 99090, 100062, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100062, 99090, 100062, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (138, 'TagA','a', 100062);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (139, 'TagI','i', 100062);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (140, 'TagK','k', 100062);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (141, 'TagT','t', 100062);
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100063, 'Fourth trigger for tag filtering', '{100063}>0', 0, 4, 0, '0', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100063, 99090, 100063, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100063, 99090, 100063, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (142, 'TagD','d', 100063);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (143, 'TagE1','e', 100063);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (144, 'TagG','g', 100063);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (145, 'TagT','t', 100063);
INSERT INTO triggers (triggerid, description, expression, value, priority, state, lastchange, comments) VALUES (100064, 'Fifth trigger for tag filtering (no tags)', '{100064}>0', 0, 5, 0, '0', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100064, 99090, 100064, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100064, 99090, 100064, 'last', '$');
-- testPageMonitoringOverview
INSERT INTO hstgrp (groupid, name, internal) VALUES (50011, 'Group to check Overview', 0);
@@ -1976,15 +1976,15 @@ INSERT INTO triggers (triggerid, description, expression, value, state, lastchan
INSERT INTO triggers (triggerid, description, expression, value, state, lastchange, comments, priority) VALUES (100038, '3_trigger_Average', '{100038}>0', 1, 0, '1533555726', 'Macro - resolved, URL - clickable: {HOST.NAME}, https://zabbix.com', 3);
INSERT INTO triggers (triggerid, description, expression, value, state, lastchange, comments, priority, url) VALUES (100039, '3_trigger_Disaster', '{100039}>0', 0, 0, '1533555726', '', 5, 'triggers.php?form=update&triggerid={TRIGGER.ID}&context=host');
INSERT INTO triggers (triggerid, description, expression, value, state, lastchange, comments, priority) VALUES (100040, '4_trigger_Average', '{100040}>0', 1, 0, '1533555726', '', 3);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100032, 99086, 100032, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100033, 99086, 100033, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100034, 99086, 100034, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100035, 99086, 100035, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100036, 99086, 100036, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100037, 99091, 100037, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100038, 99088, 100038, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100039, 99088, 100039, 'last', '0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100040, 99089, 100040, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100032, 99086, 100032, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100033, 99086, 100033, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100034, 99086, 100034, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100035, 99086, 100035, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100036, 99086, 100036, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100037, 99091, 100037, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100038, 99088, 100038, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100039, 99088, 100039, 'last', '$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100040, 99089, 100040, 'last', '$,#1');
INSERT INTO history_uint (itemid, clock, value, ns) VALUES (99086, 1533555726, 1, 726692808);
INSERT INTO history_uint (itemid, clock, value, ns) VALUES (99091, 1533555726, 2, 726692808);
INSERT INTO history_uint (itemid, clock, value, ns) VALUES (99088, 1533555726, 3, 726692808);
@@ -2038,16 +2038,16 @@ INSERT INTO items (itemid, type, hostid, name, key_, params, description, posts,
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (100001, '{16028}=0', 'A trigger', '');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (100002, '{16029}=0', 'B trigger', '');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (100003, '{16030}=0', 'C trigger', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16028, 40067, 100001,'last','0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16029, 40068, 100002,'last','0');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16030, 40069, 100003,'last','0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16028, 40067, 100001,'last','$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16029, 40068, 100002,'last','$,#1');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (16030, 40069, 100003,'last','$,#1');
-- testPageTriggers triggers filtering
INSERT INTO hosts (hostid, host, name, status, description) VALUES (99061, 'Inheritance template for triggers filtering', 'Inheritance template for triggers filtering', 3, '');
INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (99913, 99061, 1);
INSERT INTO items (itemid, type, hostid, name, description, key_, interfaceid, params, posts, headers) VALUES (99092, 2, 99061, 'Inheritance item for triggers filtering', '', 'trap', NULL, '', '', '');
INSERT INTO triggers (triggerid, description, expression, priority, state, comments) VALUES (100065, 'Inheritance trigger with tags', '{100065}>0',3, 1, '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100065, 99092, 100065, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100065, 99092, 100065, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (146, 'server','selenium', 100065);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (147, 'Street','dzelzavas', 100065);
@@ -2061,7 +2061,7 @@ INSERT INTO items (itemid, type, hostid, name, description, key_, interfaceid, p
INSERT INTO items (itemid, type, hostid, name, description, key_, interfaceid, params, posts, headers) VALUES (99094, 2, 99062, 'Item for triggers filtering', '', 'trap1', NULL, '', '', '');
INSERT INTO triggers (triggerid, description, expression, value, comments, templateid, state, error) VALUES (100066, 'Inheritance trigger with tags', '{100067}=0', 1,'', 100065, 1, 'selenium trigger cannot be evaluated for some reason');
-INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (100067, 100066, 99093, 'last', '');
+INSERT INTO functions (functionid, triggerid, itemid, name, parameter) VALUES (100067, 100066, 99093, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (148, 'server','selenium', 100066);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (149, 'Street','Dzelzavas', 100066);
INSERT INTO events (eventid, source, object, objectid, clock, ns, value, name, severity) VALUES (9008, 0, 0, 100066, 1535012391, 445429746,1, 'Inheritance trigger with tags', 3);
@@ -2071,12 +2071,12 @@ INSERT INTO problem (eventid, source, object, objectid, clock, ns, name, severit
INSERT INTO problem_tag (problemtagid, eventid, tag, value) VALUES (116, 9008, 'server', 'selenium');
INSERT INTO problem_tag (problemtagid, eventid, tag, value) VALUES (117, 9008, 'Street', 'Dzelzavas');
INSERT INTO triggers (triggerid, description, expression, status, value, priority, comments, state) VALUES (100067, 'Trigger disabled with tags', '{100067}>0', 1, 0, 3, '', 0);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100068, 99094, 100067, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100068, 99094, 100067, 'last', '$');
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (150, 'Street','Dzelzavas', 100067);
INSERT INTO trigger_tag (triggertagid, tag, value, triggerid) VALUES (151, 'country','latvia', 100067);
INSERT INTO trigger_depends (triggerdepid, triggerid_down, triggerid_up) VALUES (99000, 100066, 100067);
INSERT INTO triggers (triggerid, description, expression, status, value, priority, comments, state) VALUES (100070, 'Dependent trigger ONE', '{100067}>0', 0, 0, 4, '', 0);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100071, 99094, 100070, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100071, 99094, 100070, 'last', '$');
INSERT INTO trigger_depends (triggerdepid, triggerid_down, triggerid_up) VALUES (99001, 100070, 100067);
INSERT INTO items (itemid, type, hostid, name, description, key_, interfaceid, flags, params, posts, headers) VALUES (99095, 2, 99062, 'Discovery rule for triggers filtering', '', 'lld', NULL, 1,'','','');
@@ -2085,9 +2085,9 @@ INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, lastcheck, t
INSERT INTO items (itemid, type, hostid, name, description, key_, interfaceid, flags, params, posts, headers) VALUES (99097, 2, 99062, 'Discovered item one', '', 'lld[one]', NULL, 4, '', '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid, key_) values (15086, 99097, 99096, 'lld[one]');
INSERT INTO triggers (triggerid, description, expression, status, value, priority, comments, state, flags) VALUES (100068, 'Discovered trigger {#TEST}', '{100069}>0', 0, 0, 5, '', 0, 2);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100069, 99096, 100068, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100069, 99096, 100068, 'last', '$');
INSERT INTO triggers (triggerid, description, expression, status, value, priority, comments, state, flags) VALUES (100069, 'Discovered trigger one', '{100070}>0', 0, 0, 5, '', 0, 4);
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100070, 99097, 100069, 'last', '');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100070, 99097, 100069, 'last', '$');
INSERT INTO trigger_discovery (triggerid, parent_triggerid) VALUES (100069, 100068);
-- host/template level tags tests
@@ -2126,12 +2126,12 @@ INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (453, 14003
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (454, 14003, 'tag', 'TRIGGER');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (100113, '{100114}=0', 'Trigger with tags for cloning', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100114, 99102, 100113, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100114, 99102, 100113, 'last', '$,#1');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (186, 100113, 'action', 'clone');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (187, 100113, 'tag', 'trigger');
INSERT INTO triggers (triggerid, expression, description, comments) VALUES (100112, '{100113}=0', 'Trigger with tags for updating', '');
-INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100113, 99102, 100112, 'last', '0');
+INSERT INTO functions (functionid, itemid, triggerid, name, parameter) VALUES (100113, 99102, 100112, 'last', '$,#2');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (184, 100112, 'action', 'update');
INSERT INTO trigger_tag (triggertagid, triggerid, tag, value) VALUES (185, 100112, 'tag', 'trigger');
diff --git a/ui/tests/selenium/testFormItem.php b/ui/tests/selenium/testFormItem.php
index 7bead28a1f9..2c10e93ce7f 100644
--- a/ui/tests/selenium/testFormItem.php
+++ b/ui/tests/selenium/testFormItem.php
@@ -127,9 +127,6 @@ class testFormItem extends CLegacyWebTest {
['type' => 'Zabbix trapper', 'host' => 'Simple form test host']
],
[
- ['type' => 'Zabbix aggregate', 'host' => 'Simple form test host']
- ],
- [
['type' => 'External check', 'host' => 'Simple form test host']
],
[
@@ -271,9 +268,6 @@ class testFormItem extends CLegacyWebTest {
['type' => 'Zabbix trapper', 'template' => 'Inheritance test template']
],
[
- ['type' => 'Zabbix aggregate', 'template' => 'Inheritance test template']
- ],
- [
['type' => 'External check', 'template' => 'Inheritance test template']
],
[
@@ -395,7 +389,6 @@ class testFormItem extends CLegacyWebTest {
'SNMP trap',
'Zabbix internal',
'Zabbix trapper',
- 'Zabbix aggregate',
'External check',
'Database monitor',
'IPMI agent',
@@ -614,7 +607,6 @@ class testFormItem extends CLegacyWebTest {
case 'Simple check':
case 'SNMP agent':
case 'Zabbix internal':
- case 'Zabbix aggregate':
case 'External check':
case 'Database monitor':
case 'IPMI agent':
@@ -649,7 +641,7 @@ class testFormItem extends CLegacyWebTest {
$this->zbxTestIsEnabled("//*[@id='value_type']//li[text()='Numeric (unsigned)']");
$this->zbxTestIsEnabled("//*[@id='value_type']//li[text()='Numeric (float)']");
- if ($type == 'Zabbix aggregate' || $type == 'Calculated') {
+ if ($type == 'Calculated') {
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Character']", 'disabled');
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Log']", 'disabled');
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Text']", 'disabled');
@@ -1661,28 +1653,6 @@ class testFormItem extends CLegacyWebTest {
[
[
'expected' => TEST_GOOD,
- 'type' => 'Zabbix aggregate',
- 'name' => 'Zabbix aggregate',
- 'key' => 'grpmax[Zabbix servers group,some-item-key,last,0]',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'type' => 'Zabbix aggregate',
- 'name' => 'Zabbix aggregate',
- 'key' => 'item-zabbix-aggregate',
- 'error_msg' => 'Cannot add item',
- 'errors' => [
- 'Key "item-zabbix-aggregate" does not match'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
'type' => 'External check',
'name' => 'External check',
'key' => 'item-external-check',
@@ -2119,9 +2089,8 @@ class testFormItem extends CLegacyWebTest {
$this->zbxTestAssertElementPresentXpath("//z-select[@id='value_type']//li[text()='$value_type']");
// "Check now" button availability
- if (in_array($type, ['Zabbix agent', 'Simple check', 'SNMP agent',
- 'Zabbix internal', 'Zabbix aggregate', 'External check', 'Database monitor', 'IPMI agent',
- 'SSH agent', 'TELNET agent', 'JMX agent', 'Calculated'])) {
+ if (in_array($type, ['Zabbix agent', 'Simple check', 'SNMP agent', 'Zabbix internal', 'External check',
+ 'Database monitor', 'IPMI agent', 'SSH agent', 'TELNET agent', 'JMX agent', 'Calculated'])) {
$this->zbxTestClick('check_now');
$this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Request sent successfully');
}
diff --git a/ui/tests/selenium/testFormItemPrototype.php b/ui/tests/selenium/testFormItemPrototype.php
index dee338c9eed..77bb96c6f0c 100644
--- a/ui/tests/selenium/testFormItemPrototype.php
+++ b/ui/tests/selenium/testFormItemPrototype.php
@@ -203,12 +203,6 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'host' => 'Simple form test host',
- 'type' => 'Zabbix aggregate'
- ]
- ],
- [
- [
- 'host' => 'Simple form test host',
'type' => 'External check'
]
],
@@ -418,12 +412,6 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'template' => 'Inheritance test template',
- 'type' => 'Zabbix aggregate'
- ]
- ],
- [
- [
- 'template' => 'Inheritance test template',
'type' => 'External check'
]
],
@@ -594,7 +582,6 @@ class testFormItemPrototype extends CLegacyWebTest {
'SNMP trap',
'Zabbix internal',
'Zabbix trapper',
- 'Zabbix aggregate',
'External check',
'Database monitor',
'IPMI agent',
@@ -820,7 +807,6 @@ class testFormItemPrototype extends CLegacyWebTest {
case 'Simple check':
case 'SNMP agent':
case 'Zabbix internal':
- case 'Zabbix aggregate':
case 'External check':
case 'Database monitor':
case 'IPMI agent':
@@ -852,7 +838,7 @@ class testFormItemPrototype extends CLegacyWebTest {
'Text'
]);
- if ($type == 'Zabbix aggregate' || $type == 'Calculated') {
+ if ($type == 'Calculated') {
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Character']", 'disabled');
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Log']", 'disabled');
$this->zbxTestAssertAttribute("//*[@id='value_type']//li[text()='Text']", 'disabled');
@@ -1829,28 +1815,6 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'expected' => TEST_GOOD,
- 'type' => 'Zabbix aggregate',
- 'name' => 'Zabbix aggregate',
- 'key' => 'grpmax[Zabbix servers group,some-item-key,last,0]',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'type' => 'Zabbix aggregate',
- 'name' => 'Zabbix aggregate',
- 'key' => 'item-zabbix-aggregate',
- 'error_msg' => 'Cannot add item prototype',
- 'errors' => [
- 'Key "item-zabbix-aggregate" does not match'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
'type' => 'External check',
'name' => 'External check',
'key' => 'item-external-check',
diff --git a/ui/tests/selenium/testFormTagsTrigger.php b/ui/tests/selenium/testFormTagsTrigger.php
index 32521753388..95c101735f0 100644
--- a/ui/tests/selenium/testFormTagsTrigger.php
+++ b/ui/tests/selenium/testFormTagsTrigger.php
@@ -37,7 +37,7 @@ class testFormTagsTrigger extends testFormTags {
* @dataProvider getCreateData
*/
public function testFormTagsTrigger_Create($data) {
- $expression = '{Simple form test host:test-item-reuse.last()}=0';
+ $expression = 'last(/Simple form test host/test-item-reuse)=0';
$this->checkTagsCreate($data, 'trigger', $expression);
}
diff --git a/ui/tests/selenium/testFormTagsTriggerPrototype.php b/ui/tests/selenium/testFormTagsTriggerPrototype.php
index 0149d9d58ac..cc50e53d6d5 100644
--- a/ui/tests/selenium/testFormTagsTriggerPrototype.php
+++ b/ui/tests/selenium/testFormTagsTriggerPrototype.php
@@ -37,7 +37,7 @@ class testFormTagsTriggerPrototype extends testFormTags {
* @dataProvider getCreateData
*/
public function testFormTagsTriggerPrototype_Create($data) {
- $expression = '{Simple form test host:item-prototype-form1.last()}=0';
+ $expression = 'last(/Simple form test host/item-prototype-form1)=0';
$this->checkTagsCreate($data, 'trigger prototype', $expression);
}
diff --git a/ui/tests/selenium/testFormTrigger.php b/ui/tests/selenium/testFormTrigger.php
index dd496991213..4793dd60e6e 100644
--- a/ui/tests/selenium/testFormTrigger.php
+++ b/ui/tests/selenium/testFormTrigger.php
@@ -19,6 +19,7 @@
**/
require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
use Facebook\WebDriver\WebDriverBy;
@@ -28,6 +29,17 @@ use Facebook\WebDriver\WebDriverBy;
class testFormTrigger extends CLegacyWebTest {
/**
+ * Attach MessageBehavior to the test.
+ *
+ * @return array
+ */
+ public function getBehaviors() {
+ return [
+ 'class' => CMessageBehavior::class
+ ];
+ }
+
+ /**
* The name of the Simple form test host created in the test data set.
*
* @var string
@@ -441,7 +453,7 @@ class testFormTrigger extends CLegacyWebTest {
'expression' => '6 and 0 or 0',
'error_msg' => 'Cannot add trigger',
'errors' => [
- 'Invalid parameter "/1/expression": trigger expression must contain at least one host:key reference.'
+ 'Invalid parameter "/1/expression": trigger expression must contain at least one /host/key reference.'
]
]
],
@@ -452,7 +464,7 @@ class testFormTrigger extends CLegacyWebTest {
'expression' => '{Simple form test host}',
'error_msg' => 'Cannot add trigger',
'errors' => [
- 'Invalid parameter "/1/expression": incorrect trigger expression starting from "{Simple form test host}".'
+ 'Invalid parameter "/1/expression": incorrect expression starting from "{Simple form test host}".'
]
]
],
@@ -460,7 +472,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_simple',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -468,7 +480,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'HTML_symbols&#8704;&forall;&#8734;&ne;&sup;&Eta;&#937;&#958;&pi;&#8194;&mdash;&#8364;&loz;',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -476,7 +488,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'ASCII_characters&#33;&#40;&#51;&#101;&#10;&#25;',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -489,7 +501,7 @@ class testFormTrigger extends CLegacyWebTest {
'url' => 'http://MyTrigger_allFields.com',
'severity' => 'Disaster',
'status' => false,
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -497,7 +509,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => '1234567890',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -505,7 +517,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => '0',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -513,7 +525,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'a?aa+',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -521,7 +533,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => '}aa]a{',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -529,7 +541,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => '-aaa=%',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -537,7 +549,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'aaa,;:',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -545,7 +557,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'aaa><.',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -553,7 +565,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'aaa*&_',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -561,7 +573,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'aaa#@!',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -569,7 +581,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => '([)$^',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<0',
'formCheck' => true
]
],
@@ -577,7 +589,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_generalCheck',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<5',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<5',
'type' => true,
'comments' => 'Trigger status (expression) is recalculated every time Zabbix server receives new value, if this value is part of this expression. If time based functions are used in the expression, it is recalculated every 30 seconds by a zabbix timer process.',
'url' => 'http://www.zabbix.com',
@@ -589,7 +601,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_CheckURL',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<4',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<4',
'url' => 'triggers.php'
]
],
@@ -597,7 +609,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger_CheckUrl',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)}<5',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1)<5',
'url' => 'javascript:alert(123);',
'error_msg' => 'Cannot add trigger',
'errors' => [
@@ -609,7 +621,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Zabbix host:test-item-reuse.last(0)}<0',
+ 'expression' => 'last(/Zabbix host/test-item-reuse,#1)<0',
'error_msg' => 'Cannot add trigger',
'errors' => [
'Incorrect trigger expression. Host "Zabbix host" does not exist or you have no access to this host.'
@@ -620,7 +632,7 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:someItem.uptime.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/someItem.uptime,#1)<0',
'error_msg' => 'Cannot add trigger',
'errors' => [
'Incorrect item key "someItem.uptime" provided for trigger expression on "Simple form test host".'
@@ -631,10 +643,10 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:test-item-reuse.somefunc(0)}<0',
+ 'expression' => 'somefunc(/Simple form test host/test-item-reuse,#1)<0',
'error_msg' => 'Cannot add trigger',
'errors' => [
- 'Incorrect trigger function "somefunc(0)" provided in expression. Unknown function.'
+ 'Invalid parameter "/1/expression": unknown function "somefunc".'
]
]
],
@@ -642,10 +654,10 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)} or {#MACRO}',
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1) or {#MACRO}',
'error_msg' => 'Cannot add trigger',
'errors' => [
- 'Invalid parameter "/1/expression": incorrect trigger expression starting from " {#MACRO}".'
+ 'Invalid parameter "/1/expression": incorrect expression starting from "{#MACRO}".'
]
]
],
@@ -653,11 +665,10 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:test-item-reuse.last(0)} or {#MACRO}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/test-item-reuse,#1) or {#MACRO}',
+ 'constructor' => [
'text' => ['A or B', 'A', 'B'],
'elements' => ['expr_0_46', 'expr_51_58']
- ]
]
]
],
@@ -665,11 +676,14 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Zabbix host:test-item-reuse.last(0)}<0 or 8 and 9',
- 'constructor' => [[
+ 'expression' => 'last(/Zabbix host/test-item-reuse,#1)<0 or 8 and 9',
+ 'constructor' => [
'text' => ['A or (B and C)', 'Or', 'And', 'A', 'B', 'C'],
'elements' => ['expr_0_38', 'expr_43_43', 'expr_49_49'],
- 'elementError' => true
+ 'elementError' => true,
+ 'element_count' => 2,
+ 'errors' => [
+ 'last(/Zabbix host/test-item-reuse,#1):Unknown host, no such host present in system'
]
]
]
@@ -678,11 +692,14 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:someItem.uptime.last(0)}<0 or 8 and 9 + {Simple form test host:test-item-reuse.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/someItem,#1)<0 or 8 and 9 + last(/Simple form test host/test-item-reuse,#1)',
+ 'constructor' => [
'text' => ['A or (B and C)', 'A', 'B', 'C'],
- 'elements' => ['expr_0_48', 'expr_53_53', 'expr_59_109'],
- 'elementError' => true
+ 'elements' => ['expr_0_41', 'expr_46_46', 'expr_52_102'],
+ 'elementError' => true,
+ 'element_count' => 2,
+ 'errors' => [
+ 'last(/Simple form test host/someItem,#1):Unknown host item, no such item in selected host'
]
]
]
@@ -691,24 +708,15 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:test-item-reuse.lasta(0)}<0 or 8 and 9 + {Simple form test host:test-item-reuse.last(0)}',
- 'constructor' => [[
+ 'expression' => 'lasta(/Simple form test host/test-item-reuse,#1)<0 or 8 and 9 + last(/Simple form test host/test-item-reuse2,#1)',
+ 'constructor' => [
'text' => ['A or (B and C)', 'A', 'B', 'C'],
- 'elements' => ['expr_0_49', 'expr_54_54', 'expr_60_110'],
- 'elementError' => true
- ]
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'description' => 'MyTrigger',
- 'expression' => '{Simple form test host@:test-item-reuse.last(0)}',
- 'constructor' => [[
+ 'elements' => ['expr_0_49', 'expr_54_54', 'expr_60_111'],
+ 'elementError' => true,
+ 'element_count' => 4,
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host@:test-item-reuse.last(0)}".']
+ 'lasta(/Simple form test host/test-item-reuse,#1):Incorrect function is used',
+ 'last(/Simple form test host/test-item-reuse2,#1):Unknown host item, no such item in selected host'
]
]
]
@@ -717,11 +725,12 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:system .uptime.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host@/test-item-reuse,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:system .uptime.last(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from "last(/Simple'.
+ ' form test host@/test-item-reuse,#1)<0".'
]
]
]
@@ -730,11 +739,12 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:system .uptime.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/system .uptime,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:system .uptime.last(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from '.
+ '"last(/Simple form test host/system .uptime,#1)<0".'
]
]
]
@@ -743,11 +753,12 @@ class testFormTrigger extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:test-item-reuse.lastA(0)}',
- 'constructor' => [[
+ 'expression' => 'lastA(/Simple form test host/test-item-reuse,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:test-item-reuse.lastA(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from '.
+ '"lastA(/Simple form test host/test-item-reuse,#1)<0".'
]
]
]
@@ -836,35 +847,36 @@ class testFormTrigger extends CLegacyWebTest {
if (isset($data['constructor'])) {
$this->zbxTestClickButtonText('Expression constructor');
- foreach($data['constructor'] as $constructor) {
- if (isset($constructor['errors'])) {
- foreach($constructor['errors'] as $err) {
- $this->zbxTestWaitUntilElementVisible(WebDriverBy::className('msg-bad'));
- $this->zbxTestTextPresent($err);
+ $constructor = $data['constructor'];
+ if (isset($constructor['errors']) && !array_key_exists('elementError', $constructor)) {
+ $this->assertMessage(TEST_BAD, $constructor['errors']['header'], $constructor['errors']['details']);
+ }
+ else {
+ $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'and_expression') and text()='And']");
+ $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'or_expression') and text()='Or']");
+ $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'replace_expression') and text()='Replace']");
+
+ if (isset($constructor['text'])) {
+ foreach($constructor['text'] as $txt) {
+ $this->zbxTestTextPresent($txt);
}
}
- else {
- $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'and_expression') and text()='And']");
- $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'or_expression') and text()='Or']");
- $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'replace_expression') and text()='Replace']");
-
- if (isset($constructor['text'])) {
- foreach($constructor['text'] as $txt) {
- $this->zbxTestTextPresent($txt);
- }
- }
- if (isset($constructor['elements'])) {
- foreach($constructor['elements'] as $elem) {
- $this->zbxTestAssertElementPresentId($elem);
- }
- }
- if (isset($constructor['elementError'])) {
- $this->zbxTestAssertElementPresentXpath('//span[@class="icon-info status-red"]');
+ if (isset($constructor['elements'])) {
+ foreach($constructor['elements'] as $elem) {
+ $this->zbxTestAssertElementPresentId($elem);
}
- else {
- $this->zbxTestAssertElementNotPresentXpath('//span[@class="icon-info status-red"]');
+ }
+ if (isset($constructor['elementError'])) {
+ $count = CTestArrayHelper::get($constructor, 'element_count', 1);
+ $this->assertEquals($count, $this->query('xpath://span[@class="icon-info status-red"]')->all()->count());
+ $text = $this->query('xpath://tr[1]//div[@class="hint-box"]')->one()->getText();
+ foreach ($constructor['errors'] as $error) {
+ $this->assertContains($error, $text);
}
}
+ else {
+ $this->zbxTestAssertElementNotPresentXpath('//span[@class="icon-info status-red"]');
+ }
}
}
diff --git a/ui/tests/selenium/testFormTriggerPrototype.php b/ui/tests/selenium/testFormTriggerPrototype.php
index 4bc2e91f0c4..7b79457058e 100644
--- a/ui/tests/selenium/testFormTriggerPrototype.php
+++ b/ui/tests/selenium/testFormTriggerPrototype.php
@@ -20,6 +20,7 @@
require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
require_once dirname(__FILE__).'/../../include/items.inc.php';
+require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
use Facebook\WebDriver\WebDriverBy;
@@ -31,6 +32,17 @@ use Facebook\WebDriver\WebDriverBy;
class testFormTriggerPrototype extends CLegacyWebTest {
/**
+ * Attach MessageBehavior to the test.
+ *
+ * @return array
+ */
+ public function getBehaviors() {
+ return [
+ 'class' => CMessageBehavior::class
+ ];
+ }
+
+ /**
* The name of the test template created in the test data set.
*
* @var string
@@ -487,7 +499,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
'expression' => '{Simple form test host}',
'error_msg' => 'Cannot add trigger prototype',
'errors' => [
- 'Invalid parameter "/1/expression": incorrect trigger expression starting from "{Simple form test host}".'
+ 'Invalid parameter "/1/expression": incorrect expression starting from "{Simple form test host}".'
]
]
],
@@ -495,77 +507,77 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_sysUptime',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '1234567890',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'a?aa+',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '}aa]a{',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '-aaa=%',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa,;:',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa><.',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa*&_',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa#@!',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '([)$^',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_generalCheck',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
'type' => true,
'comments' => 'Trigger status (expression) is recalculated every time Zabbix server receives new value, if this value is part of this expression. If time based functions are used in the expression, it is recalculated every 30 seconds by a zabbix timer process. ',
'url' => 'http://www.zabbix.com',
@@ -577,7 +589,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_CheckUrl',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)}<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
'url' => 'index.php'
]
],
@@ -585,7 +597,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger_CheckWrongUrl',
- 'expression' => '{Simple form test host:someItem.uptime.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/someItem.uptime,#1)<0',
'url' => 'javascript:alert(123);',
'error_msg' => 'Cannot add trigger prototype',
'errors' => [
@@ -597,7 +609,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:someItem.uptime.last(0)}<0',
+ 'expression' => 'last(/Simple form test host/someItem.uptime,#1)<0',
'error_msg' => 'Cannot add trigger prototype',
'errors' => [
'Incorrect item key "someItem.uptime" provided for trigger expression on "Simple form test host".'
@@ -608,10 +620,10 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:item-prototype-reuse.somefunc(0)}<0',
+ 'expression' => 'somefunc(/Simple form test host/item-prototype-reuse,#1)<5',
'error_msg' => 'Cannot add trigger prototype',
'errors' => [
- 'Incorrect trigger function "somefunc(0)" provided in expression. Unknown function.'
+ 'Invalid parameter "/1/expression": unknown function "somefunc".'
]
]
],
@@ -619,11 +631,10 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:item-prototype-reuse.last(0)} or {#MACRO}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0 or {#MACRO}',
+ 'constructor' => [
'text' => ['A or B', 'A', 'B'],
- 'elements' => ['expr_0_51', 'expr_56_63']
- ]
+ 'elements' => ['expr_0_53', 'expr_58_65']
]
]
],
@@ -631,11 +642,14 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Zabbix host:item-prototype-reuse.last(0)}<0 or 8 and 9',
- 'constructor' => [[
+ 'expression' => 'last(/Zabbix host/item-prototype-reuse,#1)<0 or 8 and 9',
+ 'constructor' => [
'text' => ['A or (B and C)', 'Or', 'And', 'A', 'B', 'C'],
'elements' => ['expr_0_43', 'expr_48_48', 'expr_54_54'],
- 'elementError' => true
+ 'elementError' => true,
+ 'element_count' => 2,
+ 'errors' => [
+ 'last(/Zabbix host/item-prototype-reuse,#1):Unknown host, no such host present in system'
]
]
]
@@ -644,11 +658,14 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:someItem.uptime.last(0)}<0 or 8 and 9 + {Simple form test host:item-prototype-reuse.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/someItem.uptime,#1)<0 or 8 and 9 + last(/Simple form test host/test-item-reuse,#1)',
+ 'constructor' => [
'text' => ['A or (B and C)', 'A', 'B', 'C'],
- 'elements' => ['expr_0_48', 'expr_53_53', 'expr_59_114'],
- 'elementError' => true
+ 'elements' => ['expr_0_48', 'expr_53_53', 'expr_59_109'],
+ 'elementError' => true,
+ 'element_count' => 2,
+ 'errors' => [
+ 'last(/Simple form test host/someItem.uptime,#1):Unknown host item, no such item in selected host'
]
]
]
@@ -657,24 +674,15 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:item-prototype-reuse.lasta(0)}<0 or 8 and 9 + {Simple form test host:item-prototype-reuse.last(0)}',
- 'constructor' => [[
+ 'expression' => 'lasta(/Simple form test host/item-prototype-reuse,#1)<0 or 8 and 9 + last(/Simple form test host/test-item-reuse2,#1)',
+ 'constructor' => [
'text' => ['A or (B and C)', 'A', 'B', 'C'],
- 'elements' => ['expr_0_54', 'expr_59_59', 'expr_65_120'],
- 'elementError' => true
- ]
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'description' => 'MyTrigger',
- 'expression' => '{Simple form test host@:item-prototype-reuse.last(0)}',
- 'constructor' => [[
+ 'elements' => ['expr_0_54', 'expr_59_59', 'expr_65_116'],
+ 'elementError' => true,
+ 'element_count' => 4,
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host@:item-prototype-reuse.last(0)}".']
+ 'lasta(/Simple form test host/item-prototype-reuse,#1):Incorrect function is used',
+ 'last(/Simple form test host/test-item-reuse2,#1):Unknown host item, no such item in selected host'
]
]
]
@@ -683,11 +691,12 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:system .uptime.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host@/item-prototype-reuse,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:system .uptime.last(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from'.
+ ' "last(/Simple form test host@/item-prototype-reuse,#1)<0".'
]
]
]
@@ -696,11 +705,12 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:system .uptime.last(0)}',
- 'constructor' => [[
+ 'expression' => 'last(/Simple form test host/system .uptime,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:system .uptime.last(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from'.
+ ' "last(/Simple form test host/system .uptime,#1)<0".'
]
]
]
@@ -709,11 +719,12 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => '{Simple form test host:item-prototype-reuse.lastA(0)}',
- 'constructor' => [[
+ 'expression' => 'lastA(/Simple form test host/item-prototype-reuse,#1)<0',
+ 'constructor' => [
'errors' => [
- 'Expression syntax error.',
- 'Incorrect trigger expression. Check expression part starting from "{Simple form test host:item-prototype-reuse.lastA(0)}".']
+ 'header' => 'Expression syntax error.',
+ 'details' => 'Cannot build expression tree: incorrect expression starting from'.
+ ' "lastA(/Simple form test host/item-prototype-reuse,#1)<0".'
]
]
]
@@ -779,7 +790,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
if (isset($data['expression'])) {
switch ($data['expression']) {
case 'default':
- $expression = '{'.$this->host.':'.$this->itemKey.'.last(0)}=0';
+ $expression = 'last(/'.$this->host.'/'.$this->itemKey.',#1)=0';
$this->zbxTestInputType('expression', $expression);
break;
default:
@@ -831,37 +842,40 @@ class testFormTriggerPrototype extends CLegacyWebTest {
if (isset($data['constructor'])) {
$this->zbxTestClickButtonText('Expression constructor');
- foreach($data['constructor'] as $constructor) {
- if (isset($constructor['errors'])) {
- foreach($constructor['errors'] as $err) {
- $this->zbxTestWaitUntilElementVisible(WebDriverBy::className('msg-bad'));
- $this->zbxTestTextPresent($err);
+ $constructor = $data['constructor'];
+ if (isset($constructor['errors']) && !array_key_exists('elementError', $constructor)) {
+ $this->assertMessage(TEST_BAD, $constructor['errors']['header'], $constructor['errors']['details']);
+ }
+ else {
+ $this->zbxTestAssertElementPresentXpath("//button[@name='test_expression']");
+
+ $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'and_expression') and text()='And']");
+ $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'or_expression') and text()='Or']");
+ $this->zbxTestAssertElementPresentXpath("//button[text()='Remove']");
+
+ if (isset($constructor['text'])) {
+ foreach($constructor['text'] as $txt) {
+ $this->zbxTestTextPresent($txt);
}
}
- else {
- $this->zbxTestAssertElementPresentXpath("//button[@name='test_expression']");
-
- $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'and_expression') and text()='And']");
- $this->zbxTestAssertVisibleXpath("//li[@id='expression_row']//button[contains(@onclick, 'or_expression') and text()='Or']");
- $this->zbxTestAssertElementPresentXpath("//button[text()='Remove']");
- if (isset($constructor['elements'])) {
- foreach($constructor['elements'] as $elem) {
- $this->zbxTestAssertElementPresentId($elem);
- }
- }
- if (isset($constructor['elementError'])) {
- $this->zbxTestAssertElementPresentXpath('//span[@class="icon-info status-red"]');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath('//span[@class="icon-info status-red"]');
+
+ if (isset($constructor['elements'])) {
+ foreach($constructor['elements'] as $elem) {
+ $this->zbxTestAssertElementPresentId($elem);
}
+ }
- if (isset($constructor['text'])) {
- foreach($constructor['text'] as $txt) {
- $this->zbxTestTextPresent($txt);
- }
+ if (isset($constructor['elementError'])) {
+ $count = CTestArrayHelper::get($constructor, 'element_count', 1);
+ $this->assertEquals($count, $this->query('xpath://span[@class="icon-info status-red"]')->all()->count());
+ $text = $this->query('xpath://tr[1]//div[@class="hint-box"]')->one()->getText();
+ foreach ($constructor['errors'] as $error) {
+ $this->assertContains($error, $text);
}
}
+ else {
+ $this->zbxTestAssertElementNotPresentXpath('//span[@class="icon-info status-red"]');
+ }
}
}
diff --git a/ui/tests/selenium/testFormWeb.php b/ui/tests/selenium/testFormWeb.php
index a3cfdb8bc9f..819efb566e7 100644
--- a/ui/tests/selenium/testFormWeb.php
+++ b/ui/tests/selenium/testFormWeb.php
@@ -1528,7 +1528,7 @@ class testFormWeb extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create trigger');
$this->zbxTestInputType('description', $trigger);
- $expressionTrigger = '{'.$this->host.':'.$trigger.'.last(0)}=0';
+ $expressionTrigger = 'last(/'.$this->host.'/'.$trigger.')=0';
$this->zbxTestInputTypeWait('expression', $expressionTrigger);
$this->zbxTestClickWait('add');
diff --git a/ui/tests/selenium/testInheritanceTrigger.php b/ui/tests/selenium/testInheritanceTrigger.php
index 0fa9f92fd32..02b9cd40184 100644
--- a/ui/tests/selenium/testInheritanceTrigger.php
+++ b/ui/tests/selenium/testInheritanceTrigger.php
@@ -81,14 +81,14 @@ class testInheritanceTrigger extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'testInheritanceTrigger',
- 'expression' => '{Inheritance test template:test-inheritance-item1.last()}=0'
+ 'expression' => 'last(/Inheritance test template/test-inheritance-item1)=0'
]
],
[
[
'expected' => TEST_BAD,
'description' => 'testInheritanceTrigger1',
- 'expression' => '{Inheritance test template:key-item-inheritance-test.last()}=0',
+ 'expression' => 'last(/Inheritance test template/key-item-inheritance-test)=0',
'errors' => [
'Trigger "testInheritanceTrigger1" already exists on "Inheritance test template".'
]
diff --git a/ui/tests/selenium/testInheritanceTriggerPrototype.php b/ui/tests/selenium/testInheritanceTriggerPrototype.php
index 9463575d3ab..12d920d5810 100644
--- a/ui/tests/selenium/testInheritanceTriggerPrototype.php
+++ b/ui/tests/selenium/testInheritanceTriggerPrototype.php
@@ -78,14 +78,14 @@ class testInheritanceTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'testInheritanceTriggerPrototype5',
- 'expression' => '{Inheritance test template:item-discovery-prototype.last(0)}<0'
+ 'expression' => 'last(/Inheritance test template/item-discovery-prototype)<0'
]
],
[
[
'expected' => TEST_BAD,
'description' => 'testInheritanceTriggerPrototype1',
- 'expression' => '{Inheritance test template:key-item-inheritance-test.last()}=0',
+ 'expression' => 'last(/Inheritance test template/key-item-inheritance-test)=0',
'errors' => [
'Cannot add trigger prototype',
'Trigger prototype "testInheritanceTriggerPrototype1" must contain at least one item prototype.'
diff --git a/ui/tests/selenium/testTemplateInheritance.php b/ui/tests/selenium/testTemplateInheritance.php
index 8fb2fc63061..15a1a63e21d 100644
--- a/ui/tests/selenium/testTemplateInheritance.php
+++ b/ui/tests/selenium/testTemplateInheritance.php
@@ -209,7 +209,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create trigger');
$this->zbxTestInputTypeWait('description', 'Test LLD trigger1');
- $this->zbxTestInputType('expression', '{Inheritance test template:key-item-inheritance-test.last(0)}=0');
+ $this->zbxTestInputType('expression', 'last(/Inheritance test template/key-item-inheritance-test,#1)=0');
$this->zbxTestCheckboxSelect('type_1');
$this->zbxTestInputType('comments', 'comments');
$this->zbxTestInputType('url', 'zabbix.php');
@@ -228,7 +228,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestClickLinkTextWait('Test LLD trigger1');
$this->zbxTestAssertElementValue('description', 'Test LLD trigger1');
- $this->zbxTestAssertElementValue('expression', '{Template inheritance test host:key-item-inheritance-test.last(0)}=0');
+ $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/key-item-inheritance-test,#1)=0');
$this->assertTrue($this->zbxTestCheckboxSelected('recovery_mode_0'));
$this->zbxTestAssertElementPresentXpath("//input[@id='recovery_mode_0'][@disabled]");
$this->zbxTestAssertElementText('//*[@name="comments"]', 'comments');
@@ -420,7 +420,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create trigger prototype');
$this->zbxTestInputTypeByXpath("//input[@name='description']", 'Test LLD trigger');
- $this->zbxTestInputType('expression', '{Inheritance test template:item-discovery-prototype.last(0)}=0');
+ $this->zbxTestInputType('expression', 'last(/Inheritance test template/item-discovery-prototype,#1)=0');
$this->zbxTestCheckboxSelect('type_1');
$this->zbxTestInputType('comments', 'comments');
$this->zbxTestInputType('url', 'zabbix.php');
@@ -450,7 +450,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestWaitUntilElementVisible(WebDriverBy::id('description'));
$getName = $this->zbxTestGetValue("//input[@name='description']");
$this->assertEquals($getName, 'Test LLD trigger');
- $this->zbxTestAssertElementValue('expression', '{Template inheritance test host:item-discovery-prototype.last(0)}=0');
+ $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/item-discovery-prototype,#1)=0');
$this->assertTrue($this->zbxTestCheckboxSelected('recovery_mode_0'));
$this->zbxTestAssertElementPresentXpath("//input[@id='recovery_mode_0'][@disabled]");
$this->zbxTestAssertElementText('//*[@name="comments"]', 'comments');
diff --git a/ui/tests/unit/bootstrap.php b/ui/tests/unit/bootstrap.php
index 719105ab470..16bf535a0a4 100644
--- a/ui/tests/unit/bootstrap.php
+++ b/ui/tests/unit/bootstrap.php
@@ -41,6 +41,7 @@ $autoloader->addNamespace('', [
__DIR__.'/../../include/classes/api/clients',
__DIR__.'/../../include/classes/api/helpers',
__DIR__.'/../../include/classes/api/wrappers',
+ __DIR__.'/../../include/classes/data',
__DIR__.'/../../include/classes/core',
__DIR__.'/../../include/classes/helpers',
__DIR__.'/../../include/classes/db',
diff --git a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
index bbbd7b38a10..2a3bddc51f3 100644
--- a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
+++ b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
@@ -649,8 +649,7 @@ class CImportDataAdapterTest extends TestCase {
$this->assertEquals($adapter->getTriggers(), [
[
'uuid' => 'fe9cb3ea1d4d485c9ff7f8a749dc9380',
-
- 'expression' => '{export-host:item.last(0)}<>0',
+ 'expression' => 'last(/export-host/item)<>0',
'recovery_mode' => (string) ZBX_RECOVERY_MODE_EXPRESSION,
'recovery_expression' => '',
'url' => '',
@@ -660,7 +659,7 @@ class CImportDataAdapterTest extends TestCase {
'dependencies' =>[
[
'name' => 'trigger2',
- 'expression' => '{export-host:item.last(0)}<>0',
+ 'expression' => 'last(/export-host/item)<>0',
'recovery_expression' => ''
]
],
@@ -675,7 +674,7 @@ class CImportDataAdapterTest extends TestCase {
],
[
'uuid' => 'ba2b1992cfcc4c3c84d7908a9afd3f3d',
- 'expression' => '{export-host:item.last(0)}<>0',
+ 'expression' => 'last(/export-host/item)<>0',
'recovery_mode' => (string) ZBX_RECOVERY_MODE_EXPRESSION,
'recovery_expression' => '',
'url' => '',
@@ -694,7 +693,7 @@ class CImportDataAdapterTest extends TestCase {
],
[
'uuid' => 'cd79d05182bc4974b745f0076a4edef4',
- 'expression' => '{export-template:item.last(0)}<>0',
+ 'expression' => 'last(/export-template/item)<>0',
'recovery_mode' => (string) ZBX_RECOVERY_MODE_EXPRESSION,
'recovery_expression' => '',
'url' => '',
@@ -704,7 +703,7 @@ class CImportDataAdapterTest extends TestCase {
'dependencies' =>[
[
'name' => 'trigger2',
- 'expression' => '{export-template:item.last(0)}<>0',
+ 'expression' => 'last(/export-template/item)<>0',
'recovery_expression' => ''
]
],
@@ -719,7 +718,7 @@ class CImportDataAdapterTest extends TestCase {
],
[
'uuid' => 'b43057d7c32e4698a462143b889cfe87',
- 'expression' => '{export-template:item.last(0)}<>0',
+ 'expression' => 'last(/export-template/item)<>0',
'recovery_mode' => (string) ZBX_RECOVERY_MODE_EXPRESSION,
'recovery_expression' => '',
'url' => '',
@@ -1000,6 +999,7 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item',
+ 'trigger_prototypes' => [],
'trapper_hosts' => '',
'preprocessing' => []
],
@@ -1049,6 +1049,7 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item-jmx',
+ 'trigger_prototypes' => [],
'trapper_hosts' => '',
'preprocessing' => []
],
@@ -1103,13 +1104,14 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item2',
+ 'trigger_prototypes' => [],
'trapper_hosts' => '',
'preprocessing' => []
]
],
'trigger_prototypes' => [
[
- 'expression' => '{export-host:lld-item.last()}=0',
+ 'expression' => 'last(/export-host/lld-item)=0',
'description' => 'lld-trigger',
'url' => '',
'discover' => '0',
@@ -1401,6 +1403,7 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
],
[
@@ -1450,6 +1453,7 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item-jmx',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
],
[
@@ -1504,13 +1508,14 @@ class CImportDataAdapterTest extends TestCase {
'parameters' => [],
'headers' => [],
'key_' => 'lld-item2',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
'trigger_prototypes' => [
[
'uuid' => 'e0ead3b167ea4af3a4a1766db2ffc6dc',
- 'expression' => '{export-template:lld-item.last()}=0',
+ 'expression' => 'last(/export-template/lld-item)=0',
'description' => 'lld-trigger',
'url' => '',
'discover' => '0',
@@ -1769,7 +1774,7 @@ class CImportDataAdapterTest extends TestCase {
'elements' => [
[
'description' => 'trigger',
- 'expression' => '{export-host:item.last(0)}<>0 or {export-host:item.last(0)}<>0 and {export-host:item.last(0)}<>0',
+ 'expression' => 'last(/export-host/item)<>0 or last(/export-host/item)<>0 and last(/export-host/item)<>0',
'recovery_expression' => ''
]
]
@@ -1856,7 +1861,7 @@ class CImportDataAdapterTest extends TestCase {
'color' => 'DD0000',
'trigger' => [
'description' => 'trigger',
- 'expression' => '{export-host:item.last(0)}<>0 or {export-host:item.last(0)}<>0 and {export-host:item.last(0)}<>0',
+ 'expression' => 'last(/export-host/item)<>0 or last(/export-host/item)<>0 and last(/export-host/item)<>0',
'recovery_expression' => ''
]
]
@@ -2233,7 +2238,7 @@ class CImportDataAdapterTest extends TestCase {
[
'uuid' => '8aece41340ce47ecab3e0ac69313fb6d',
'type' => '0',
- 'expression' => '{Template_Linux:vfs.fs.size[/,pfree].last(0)}<10',
+ 'expression' => 'last(/Template_Linux/vfs.fs.size[/,pfree])<10',
'url' => 'http://www.zabbix.com/',
'status' => '0',
'priority' => '4',
@@ -2252,7 +2257,7 @@ class CImportDataAdapterTest extends TestCase {
[
'uuid' => '0117c941adb04a728bb79e156179f97f',
'type' => '1',
- 'expression' => '{Template_Simple:net.tcp.service[ftp,,21].last(0)}<>0 or {Template_Simple:net.tcp.service[ftp,,{$PORT.FTP}].last(0)}<>0',
+ 'expression' => 'last(/Template_Simple/net.tcp.service[ftp,,21])<>0 or last(/Template_Simple/net.tcp.service[ftp,,{$PORT.FTP}])<>0',
'url' => 'triggers.php',
'status' => '1',
'priority' => '3',
@@ -3197,6 +3202,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test7',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
@@ -3298,6 +3304,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test8',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
@@ -3399,6 +3406,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test9',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
@@ -3992,6 +4000,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test7',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
@@ -4093,6 +4102,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test8',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
@@ -4194,6 +4204,7 @@ class CImportDataAdapterTest extends TestCase {
'verify_peer' => '0',
'verify_host' => '0',
'key_' => 'test9',
+ 'trigger_prototypes' => [],
'trapper_hosts' => ''
]
],
diff --git a/ui/tests/unit/include/classes/import/converters/C52AggregateItemKeyConverterTest.php b/ui/tests/unit/include/classes/import/converters/C52AggregateItemKeyConverterTest.php
new file mode 100644
index 00000000000..b50761baf08
--- /dev/null
+++ b/ui/tests/unit/include/classes/import/converters/C52AggregateItemKeyConverterTest.php
@@ -0,0 +1,95 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class C52AggregateItemKeyConverterTest extends TestCase {
+
+ protected $converter;
+
+ protected function setUp(): void {
+ $this->converter = new C52AggregateItemKeyConverter();
+ }
+
+ public function dataProvider(): array {
+ return [
+ [
+ 'grpsum["MySQL Servers","vfs.fs.size[/,total]",last]',
+ 'sum(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"]))'
+ ],
+ [
+ 'grpavg["MySQL Servers","system.cpu.load[,avg1]",last]',
+ 'avg(last_foreach(/*/system.cpu.load[,avg1]?[group="MySQL Servers"]))'
+ ],
+ [
+ 'grpavg["MySQL Servers",mysql.qps,avg,5m]',
+ 'avg(avg_foreach(/*/mysql.qps?[group="MySQL Servers"],5m))'
+ ],
+ [
+ 'grpavg[["Servers A","Servers B","Servers C"],system.cpu.load,last]',
+ 'avg(last_foreach(/*/system.cpu.load?[group="Servers A" or group="Servers B" or group="Servers C"]))'
+ ],
+ [
+ 'grpsum["My, group","trap1",last]',
+ 'sum(last_foreach(/*/trap1?[group="My, group"]))'
+ ],
+ [
+ 'grpsum["MySQL\\"Servers","vfs.fs.size[/,total]",last]',
+ 'sum(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL\\"Servers"]))'
+ ],
+ [
+ 'grpsum["MySQL\\Servers","trap1",last]',
+ 'sum(last_foreach(/*/trap1?[group="MySQL\\Servers"]))'
+ ],
+ [
+ 'grpsum[ "Zabbix servers" , trap1 , last, 30s ]',
+ 'sum(last_foreach(/*/trap1?[group="Zabbix servers"],30s))'
+ ],
+ [
+ 'grpavg[{$M},trap1,avg,{$M3}s]',
+ 'avg(avg_foreach(/*/trap1?[group="{$M}"],{$M3}s))'
+ ],
+ [
+ 'grpfunc["Host group",item key,func1,timeperiod]',
+ 'func(func1_foreach(/*/item key?[group="Host group"],timeperiod))'
+ ],
+
+ [
+ 'simplekey',
+ 'simplekey'
+ ],
+ [
+ 'grpmin[]',
+ 'grpmin[]'
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param $key
+ * @param $expected
+ */
+ public function testConvert($key, $expected) {
+ $this->assertEquals($expected, $this->converter->convert($key));
+ }
+}
diff --git a/ui/tests/unit/include/classes/import/converters/C52EventNameConverterTest.php b/ui/tests/unit/include/classes/import/converters/C52EventNameConverterTest.php
new file mode 100644
index 00000000000..f2768eb0afa
--- /dev/null
+++ b/ui/tests/unit/include/classes/import/converters/C52EventNameConverterTest.php
@@ -0,0 +1,92 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class C52EventNameConverterTest extends TestCase {
+
+ /**
+ * @var C52EventNameConverter
+ */
+ private $converter;
+
+ protected function setUp(): void {
+ $this->converter = new C52EventNameConverter();
+ }
+
+ protected function tearDown(): void {
+ $this->converter = null;
+ }
+
+ public function simpleProviderData() {
+ return [
+ [
+ 'String containing expression macro {?{host:item.last()} = 0}.',
+ 'String containing expression macro {?last(/host/item) = 0}.'
+ ],
+ [
+ 'String containing expression macro {?{host:item[{#M}].last()} = 0}.',
+ 'String containing expression macro {?last(/host/item[{#M}]) = 0}.'
+ ],
+ [
+ 'String containing expression macro {?{{HOST.HOST}:item.func(1)} = 0}.',
+ 'String containing expression macro {?func(//item,"1") = 0}.'
+ ],
+ [
+ 'String containing expression macro '.
+ '{{?100*'.
+ '{Zabbux Server:system.cpu.load.trendavg(1M,now/M)}'.
+ '/'.
+ '{Zabbux Server:system.cpu.load.trendavg(1M,now/M-1M)}'.
+ '}.fmtnum(0)}'.
+ '%',
+
+ 'String containing expression macro {{?100*'.
+ 'trendavg(/Zabbux Server/system.cpu.load,1M:now/M)'.
+ '/'.
+ 'trendavg(/Zabbux Server/system.cpu.load,1M:now/M-1M)'.
+ '}.fmtnum(0)}%'
+ ],
+ [
+ 'String containing expression macro {?{host:item.date()}}=1 and {?{host:item.date()}}.',
+ 'String containing expression macro {?date()}=1 and {?date()}.'
+ ],
+ [
+ 'String containing expression macro {?{host:item.date()}}=1.',
+ 'String containing expression macro {?date()}=1.'
+ ],
+ [
+ 'String containing expression macro {?{host:item.date()}=1}.',
+ 'String containing expression macro {?date()=1}.'
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider simpleProviderData
+ *
+ * @param string $old_event_name
+ * @param string $new_event_name
+ */
+ public function testSimpleConversion(string $old_event_name, string $new_event_name) {
+ $this->assertSame($new_event_name, $this->converter->convert($old_event_name, '', ''));
+ }
+}
diff --git a/ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php b/ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php
index cae4240e4c4..5b39b7d0423 100644
--- a/ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php
+++ b/ui/tests/unit/include/classes/import/converters/C52ImportConverterTest.php
@@ -1,4 +1,4 @@
-<?php declare(strict_types=1);
+<?php declare(strict_types = 1);
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -19,10 +19,376 @@
**/
-use PHPUnit\Framework\TestCase;
-
class C52ImportConverterTest extends CImportConverterTest {
+ public function importConvertProviderData(): array {
+ $calculated = ['type' => CXmlConstantName::CALCULATED, 'key' => ''];
+
+ return [
+ [
+ [],
+ []
+ ],
+ [
+ [
+ 'templates' => [
+ [
+ 'template' => 'Template',
+ 'items' => [
+ $calculated + ['params' => '100*last("vfs.fs.size[/,free]")/last("vfs.fs.size[/,total]")'],
+ $calculated + ['params' => 'avg("Zabbix Server:zabbix[wcache,values]",600)'],
+ $calculated + ['params' => 'last("net.if.in[eth0,bytes]")+last("net.if.out[eth0,bytes]")'],
+ $calculated + ['params' => '100*last("net.if.in[eth0,bytes]")/(last("net.if.in[eth0,bytes]")+last("net.if.out[eth0,bytes]"))'],
+ $calculated + ['params' => 'last("grpsum[\\"video\\",\\"net.if.out[eth0,bytes]\\",\\"last\\"]") / last("grpsum[\\"video\\",\\"nginx_stat.sh[active]\\",\\"last\\"]")'],
+ $calculated + ['params' => 'last(es.node.indices.flush.total_time_in_millis[{#ES.NODE}]) / ( last(es.node.indices.flush.total[{#ES.NODE}]) + (last(es.node.indices.flush.total[{#ES.NODE}]) = 0) )'],
+ $calculated + ['params' => 'last(haproxy.frontend.scur[{#PXNAME}:{#SVNAME}]) / last(haproxy.frontend.slim[{#PXNAME}:{#SVNAME}]) * 100'],
+ $calculated + ['params' => 'last(php-fpm.listen_queue)/(last(php-fpm.listen_queue_len)+last(php-fpm.listen_queue_len)=0)*100'],
+ $calculated + ['params' => 'last("vm.memory.used[hrStorageUsed.{#SNMPINDEX}]")/last("vm.memory.total[hrStorageSize.{#SNMPINDEX}]")*100'],
+ $calculated + ['params' => 'last("system.swap.size[,total]") - last("system.swap.size[,total]") / 100 * last("perf_counter_en[\\"\\Paging file(_Total)\\% Usage\\"]")'],
+ $calculated + ['params' => 'avg("zbxnext_6451:'."\n\r\n".'agent_numeric[wcache,values]",600)'],
+ $calculated + ['params' => 'abschange("trap1") + avg(trap1,1h,1d) + band(trap1,,12)=4 + count(trap1,10m) + count(trap1,10m,"error",eq) + count(trap1,10m,12) + count(trap1, 10m,12,gt) + count(trap1, #10,12,gt) + count(trap1, 10m,12,gt,1d) + count(trap1,10m,6/7,band) + count(trap1, 10m,,,1d) + count(trap1,10m,"56",eq) + count("Zabbix server:trap3",10m,error,eq) + date("trap1") + dayofmonth(trap1) + dayofweek(trap1) + delta(trap1,30s) + diff(trap1) + forecast(trap1,#10,,1h) + forecast(trap1,1h,,30m) + forecast(trap1,1h,1d,12h) + forecast(trap1,1h,,10m,exponential) + forecast(trap1,1h,,2h,polynomial3,max) + fuzzytime(trap1,40) + count(trap2,10m,56,eq)']
+ ]
+ ]
+ ]
+ ],
+ [
+ 'templates' => [
+ [
+ 'template' => 'Template',
+ 'uuid' => generateUuidV4('Template'),
+ 'items' => [
+ $calculated + ['params' => '100*last(/'.'/vfs.fs.size[/,free])/last(/'.'/vfs.fs.size[/,total])'],
+ $calculated + ['params' => 'avg(/Zabbix Server/zabbix[wcache,values],600s)'],
+ $calculated + ['params' => 'last(/'.'/net.if.in[eth0,bytes])+last(/'.'/net.if.out[eth0,bytes])'],
+ $calculated + ['params' => '100*last(/'.'/net.if.in[eth0,bytes])/(last(/'.'/net.if.in[eth0,bytes])+last(/'.'/net.if.out[eth0,bytes]))'],
+ $calculated + ['params' => 'last(/'.'/grpsum["video","net.if.out[eth0,bytes]","last"]) / last(/'.'/grpsum["video","nginx_stat.sh[active]","last"])'],
+ $calculated + ['params' => 'last(/'.'/es.node.indices.flush.total_time_in_millis[{#ES.NODE}]) / ( last(/'.'/es.node.indices.flush.total[{#ES.NODE}]) + (last(/'.'/es.node.indices.flush.total[{#ES.NODE}]) = 0) )'],
+ $calculated + ['params' => 'last(/'.'/haproxy.frontend.scur[{#PXNAME}:{#SVNAME}]) / last(/'.'/haproxy.frontend.slim[{#PXNAME}:{#SVNAME}]) * 100'],
+ $calculated + ['params' => 'last(/'.'/php-fpm.listen_queue)/(last(/'.'/php-fpm.listen_queue_len)+last(/'.'/php-fpm.listen_queue_len)=0)*100'],
+ $calculated + ['params' => 'last(/'.'/vm.memory.used[hrStorageUsed.{#SNMPINDEX}])/last(/'.'/vm.memory.total[hrStorageSize.{#SNMPINDEX}])*100'],
+ $calculated + ['params' => 'last(/'.'/system.swap.size[,total]) - last(/'.'/system.swap.size[,total]) / 100 * last(/'.'/perf_counter_en["\\Paging file(_Total)\% Usage"])'],
+ $calculated + ['params' => 'avg(/zbxnext_6451/agent_numeric[wcache,values],600s)'],
+ $calculated + ['params' => 'abs(change(/'.'/trap1)) + avg(/'.'/trap1,1h:now-1d) + bitand(last(/'.'/trap1),12)=4 + count(/'.'/trap1,10m) + count(/'.'/trap1,10m,"eq","error") + count(/'.'/trap1,10m,,"12") + count(/'.'/trap1,10m,"gt","12") + count(/'.'/trap1,#10,"gt","12") + count(/'.'/trap1,10m:now-1d,"gt","12") + count(/'.'/trap1,10m,"bitand","6/7") + count(/'.'/trap1,10m:now-1d) + count(/'.'/trap1,10m,"eq","56") + count(/Zabbix server/trap3,10m,"eq","error") + date() + dayofmonth() + dayofweek() + (max(/'.'/trap1,30s)-min(/'.'/trap1,30s)) + (last(/'.'/trap1,#1)<>last(/'.'/trap1,#2)) + forecast(/'.'/trap1,#10,1h) + forecast(/'.'/trap1,1h,30m) + forecast(/'.'/trap1,1h:now-1d,12h) + forecast(/'.'/trap1,1h,10m,"exponential") + forecast(/'.'/trap1,1h,2h,"polynomial3","max") + fuzzytime(/'.'/trap1,40s) + count(/'.'/trap2,10m,"eq","56")']
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'key',
+ 'triggers' => [
+ [
+ 'expression' => '{min(5m)}=1',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'key',
+ 'triggers' => [
+ [
+ 'expression' => 'min(/hostname/key,5m)=1',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'triggers' => [
+ [
+ 'expression' => '{hostname:key.min(5m)}=1',
+ 'recovery_expression' => '{hostname:key.min(5m)}<>1'
+ ]
+ ]
+ ],
+ [
+ 'triggers' => [
+ [
+ 'expression' => 'min(/hostname/key,5m)=1',
+ 'recovery_expression' => 'min(/hostname/key,5m)<>1'
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'grpmin["host group","item",last,5m]',
+ 'type' => 'AGGREGATE'
+ ]
+ ]
+ ]
+ ],
+ 'triggers' => [
+ [
+ 'expression' => '{hostname:grpmin["host group","item",last,5m].last()}=5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ],
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'grpmin["host group","item",last,5m]',
+ 'type' => 'CALCULATED',
+ 'params' => 'min(last_foreach(/*/item?[group="host group"],5m))'
+ ]
+ ]
+ ]
+ ],
+ 'triggers' => [
+ [
+ 'expression' => 'last(/hostname/grpmin["host group","item",last,5m])=5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'grpmin["host group","item",last,5m]',
+ 'type' => 'AGGREGATE'
+ ]
+ ]
+ ]
+ ],
+ 'triggers' => [
+ [
+ 'expression' => '{hostname:grpmin["host group","item",last,5m].date()}=5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ],
+ [
+ 'hosts' => [
+ [
+ 'host' => 'hostname',
+ 'items' => [
+ 'item' => [
+ 'key' => 'grpmin["host group","item",last,5m]',
+ 'type' => 'CALCULATED',
+ 'params' => 'min(last_foreach(/*/item?[group="host group"],5m))'
+ ]
+ ]
+ ]
+ ],
+ 'triggers' => [
+ [
+ 'expression' => '(date()=5) or (last(/hostname/grpmin["host group","item",last,5m])<>last(/hostname/grpmin["host group","item",last,5m]))',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'triggers' => [
+ [
+ 'event_name' => '{?{k:grpmin["zn6451","item",last,5m].last()}}',
+ 'expression' => '{k:system.cpu.load.last(5s, 1d)} > 5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ],
+ [
+ 'triggers' => [
+ [
+ 'event_name' => '{?last(/k/grpmin["zn6451","item",last,5m])}',
+ 'expression' => 'last(/k/system.cpu.load,#1:now-1d) > 5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'triggers' => [
+ [
+ 'event_name' => '{?{k:grpmin["zn6451","item",last,5m].date()}}',
+ 'expression' => '{k:system.cpu.load.last(5s)} > 5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ],
+ [
+ 'triggers' => [
+ [
+ 'event_name' => '{?date()}',
+ 'expression' => 'last(/k/system.cpu.load) > 5',
+ 'recovery_expression' => ''
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'maps' => [
+ [
+ 'name' => 'Local network',
+ 'selements' => [
+ [
+ 'elementtype' => '0',
+ 'elements' => [
+ [
+ 'host' => 'Zabbix server'
+ ]
+ ],
+ 'application' => 'MySQL'
+ ],
+ [
+ 'elementtype' => '2',
+ 'elements' => [
+ [
+ 'description' => 'trigger',
+ 'expression' => '{Zabbix server:proc.num.last()} = 0',
+ 'recovery_expression' => '{Zabbix server:proc.num.last()} <> 0'
+ ]
+ ],
+ 'application' => ''
+ ]
+ ],
+ 'links' => [
+ [
+ 'linktriggers' => [
+ [
+ 'trigger' => [
+ 'description' => 'trigger',
+ 'expression' => '{Zabbix server:proc.num.last()} = 0',
+ 'recovery_expression' => '{Zabbix server:proc.num.last()} <> 0'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'maps' => [
+ [
+ 'name' => 'Local network',
+ 'selements' => [
+ [
+ 'elementtype' => '0',
+ 'elements' => [
+ [
+ 'host' => 'Zabbix server'
+ ]
+ ],
+ 'evaltype' => '0',
+ 'tags' => [
+ 'tag' => [
+ 'tag' => 'Application',
+ 'value' => 'MySQL',
+ 'operator' => '0'
+ ]
+ ]
+ ],
+ [
+ 'elementtype' => '2',
+ 'elements' => [
+ [
+ 'description' => 'trigger',
+ 'expression' => 'last(/Zabbix server/proc.num) = 0',
+ 'recovery_expression' => 'last(/Zabbix server/proc.num) <> 0'
+ ]
+ ],
+ 'evaltype' => '0'
+ ]
+ ],
+ 'links' => [
+ [
+ 'linktriggers' => [
+ [
+ 'trigger' => [
+ 'description' => 'trigger',
+ 'expression' => 'last(/Zabbix server/proc.num) = 0',
+ 'recovery_expression' => 'last(/Zabbix server/proc.num) <> 0'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ $this->getGroupsUuidData(),
+ $this->getTemplateItemsUuidData(),
+ $this->getTriggerUuidData(),
+ $this->getGraphUuidData(),
+ $this->getTemplateDashboardUuidData(),
+ $this->getHttptestUuidData(),
+ $this->getValuemapsUuidData(),
+ $this->getDiscoveryRuleNameUuidData(),
+ $this->getItemPrototypeUuidData(),
+ $this->getTriggerPrototypeUuidData(),
+ $this->getGraphPrototypeUuidData(),
+ $this->getHostPrototypeUuidData(),
+ $this->getHostUuidData()
+ ];
+ }
+
+ /**
+ * @dataProvider importConvertProviderData
+ *
+ * @param array $data
+ * @param array $expected
+ */
+ public function testConvert(array $data, array $expected): void {
+ $this->assertConvert($this->createExpectedResult($expected), $this->createSource($data));
+ }
+
+ protected function createSource(array $data = []): array {
+ return [
+ 'zabbix_export' => array_merge([
+ 'version' => '5.2',
+ 'date' => '2020-01-01T00:00:00Z'
+ ], $data)
+ ];
+ }
+
+ protected function createExpectedResult(array $data = []): array {
+ return [
+ 'zabbix_export' => array_merge([
+ 'version' => '5.4',
+ 'date' => '2020-01-01T00:00:00Z'
+ ], $data)
+ ];
+ }
+
protected function getTemplateNameUuidData(): array {
/**
* For creation UUID's, template names will have to be updated by removing following part (regex):
@@ -33,18 +399,18 @@ class C52ImportConverterTest extends CImportConverterTest {
return [
[
'templates' => [
- ['name' => 'Template OS'],
- ['name' => 'Template OS ab'],
- ['name' => 'Template OS abc'],
- ['name' => 'Template MODULE abc'],
+ ['template' => 'Template OS'],
+ ['template' => 'Template OS ab'],
+ ['template' => 'Template OS abc'],
+ ['template' => 'Template MODULE abc'],
]
],
[
'templates' => [
- ['name' => 'Template OS', 'uuid' => generateUuidV4('Template OS')],
- ['name' => 'Template OS ab', 'uuid' => generateUuidV4('Template OS ab')],
- ['name' => 'Template OS abc', 'uuid' => generateUuidV4('abc')],
- ['name' => 'Template MODULE abc', 'uuid' => generateUuidV4('Template MODULE abc')]
+ ['template' => 'Template OS', 'uuid' => generateUuidV4('Template OS')],
+ ['template' => 'Template OS ab', 'uuid' => generateUuidV4('Template OS ab')],
+ ['template' => 'Template OS abc', 'uuid' => generateUuidV4('abc')],
+ ['template' => 'Template MODULE abc', 'uuid' => generateUuidV4('Template MODULE abc')]
]
]
];
@@ -58,10 +424,10 @@ class C52ImportConverterTest extends CImportConverterTest {
return [
[
'hosts' => [
- ['name' => 'Host A', 'groups' => [['name' => 'Group B']]]
+ ['host' => 'Host A', 'groups' => [['name' => 'Group B']]]
],
'templates' => [
- ['name' => 'Template A', 'groups' => [['name' => 'Group A']]]
+ ['template' => 'Template A', 'groups' => [['name' => 'Group A']]]
],
'groups' => [
['name' => 'Group A'],
@@ -70,10 +436,10 @@ class C52ImportConverterTest extends CImportConverterTest {
],
[
'hosts' => [
- ['name' => 'Host A', 'groups' => [['name' => 'Group B']]]
+ ['host' => 'Host A', 'groups' => [['name' => 'Group B']]]
],
'templates' => [
- ['name' => 'Template A', 'groups' => [['name' => 'Group A']], 'uuid' => generateUuidV4('Template A')]
+ ['template' => 'Template A', 'groups' => [['name' => 'Group A']], 'uuid' => generateUuidV4('Template A')]
],
'groups' => [
['name' => 'Group A', 'uuid' => generateUuidV4('Group A')],
@@ -89,13 +455,13 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template C',
+ 'template' => 'Template C',
'items' => [
['key' => 'item1']
]
],
[
- 'name' => 'Template OS Old name D',
+ 'template' => 'Template OS Old name D',
'items' => [
['key' => 'item1']
]
@@ -105,14 +471,14 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template C',
+ 'template' => 'Template C',
'items' => [
['key' => 'item1', 'uuid' => generateUuidV4('Template C/item1')]
],
'uuid' => generateUuidV4('Template C')
],
[
- 'name' => 'Template OS Old name D',
+ 'template' => 'Template OS Old name D',
'items' => [
['key' => 'item1', 'uuid' => generateUuidV4('Old name D/item1')]
],
@@ -129,7 +495,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template E',
+ 'template' => 'Template E',
'items' => [
[
'key' => 'item2',
@@ -152,7 +518,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template E',
+ 'template' => 'Template E',
'items' => [
[
'key' => 'item2',
@@ -184,8 +550,8 @@ class C52ImportConverterTest extends CImportConverterTest {
return [
[
'templates' => [
- ['name' => 'Template OS'],
- ['name' => 'Template OS Template G']
+ ['template' => 'Template OS'],
+ ['template' => 'Template OS Template G']
],
'graphs' => [
[
@@ -199,8 +565,8 @@ class C52ImportConverterTest extends CImportConverterTest {
],
[
'templates' => [
- ['name' => 'Template OS', 'uuid' => generateUuidV4('Template OS')],
- ['name' => 'Template OS Template G', 'uuid' => generateUuidV4('Template G')]
+ ['template' => 'Template OS', 'uuid' => generateUuidV4('Template OS')],
+ ['template' => 'Template OS Template G', 'uuid' => generateUuidV4('Template G')]
],
'graphs' => [
[
@@ -222,13 +588,13 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template H',
+ 'template' => 'Template H',
'dashboards' => [
['name' => 'Dashboard A']
]
],
[
- 'name' => 'Template OS Template I',
+ 'template' => 'Template OS Template I',
'dashboards' => [
['name' => 'Dashboard B']
]
@@ -238,14 +604,14 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template H',
+ 'template' => 'Template H',
'dashboards' => [
['name' => 'Dashboard A', 'uuid' => generateUuidV4('Template H/Dashboard A'), 'pages' => [[]]]
],
'uuid' => generateUuidV4('Template H')
],
[
- 'name' => 'Template OS Template I',
+ 'template' => 'Template OS Template I',
'dashboards' => [
['name' => 'Dashboard B', 'uuid' => generateUuidV4('Template I/Dashboard B'), 'pages' => [[]]]
],
@@ -262,13 +628,13 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template J',
+ 'template' => 'Template J',
'httptests' => [
['name' => 'HTTP Test A']
]
],
[
- 'name' => 'Template OS Template K',
+ 'template' => 'Template OS Template K',
'httptests' => [
['name' => 'HTTP Test B']
]
@@ -278,14 +644,14 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template J',
+ 'template' => 'Template J',
'httptests' => [
['name' => 'HTTP Test A', 'uuid' => generateUuidV4('Template J/HTTP Test A')]
],
'uuid' => generateUuidV4('Template J')
],
[
- 'name' => 'Template OS Template K',
+ 'template' => 'Template OS Template K',
'httptests' => [
['name' => 'HTTP Test B', 'uuid' => generateUuidV4('Template K/HTTP Test B')]
],
@@ -302,14 +668,14 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template L',
+ 'template' => 'Template L',
'items' => [
['key' => 'item5', 'valuemap' => ['name' => 'Value map A']],
['key' => 'item6', 'valuemap' => ['name' => 'Value map B']]
]
],
[
- 'name' => 'Template OS Template K',
+ 'template' => 'Template OS Template K',
'items' => [
['key' => 'item7', 'valuemap' => ['name' => 'Value map A']],
['key' => 'item8', 'valuemap' => ['name' => 'Value map B']]
@@ -324,7 +690,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template L',
+ 'template' => 'Template L',
'items' => [
['key' => 'item5', 'valuemap' => ['name' => 'Value map A'], 'uuid' => generateUuidV4('Template L/item5')],
['key' => 'item6', 'valuemap' => ['name' => 'Value map B'], 'uuid' => generateUuidV4('Template L/item6')]
@@ -336,7 +702,7 @@ class C52ImportConverterTest extends CImportConverterTest {
'uuid' => generateUuidV4('Template L')
],
[
- 'name' => 'Template OS Template K',
+ 'template' => 'Template OS Template K',
'items' => [
['key' => 'item7', 'valuemap' => ['name' => 'Value map A'], 'uuid' => generateUuidV4('Template K/item7')],
['key' => 'item8', 'valuemap' => ['name' => 'Value map B'], 'uuid' => generateUuidV4('Template K/item8')]
@@ -358,13 +724,13 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template M',
+ 'template' => 'Template M',
'discovery_rules' => [
['key' => 'drule1']
]
],
[
- 'name' => 'Template OS Template N',
+ 'template' => 'Template OS Template N',
'discovery_rules' => [
['key' => 'drule2']
]
@@ -374,14 +740,14 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template M',
+ 'template' => 'Template M',
'discovery_rules' => [
['key' => 'drule1', 'uuid' => generateUuidV4('Template M/drule1')]
],
'uuid' => generateUuidV4('Template M')
],
[
- 'name' => 'Template OS Template N',
+ 'template' => 'Template OS Template N',
'discovery_rules' => [
['key' => 'drule2', 'uuid' => generateUuidV4('Template N/drule2')]
],
@@ -392,19 +758,19 @@ class C52ImportConverterTest extends CImportConverterTest {
];
}
- protected function getItemProtypeUuidData(): array {
+ protected function getItemPrototypeUuidData(): array {
// Use "template name/discovery rule key/item prototype key".
return [
[
'templates' => [
[
- 'name' => 'Template O',
+ 'template' => 'Template O',
'discovery_rules' => [
['key' => 'drule3', 'item_prototypes' => [['key' => 'item9']]]
]
],
[
- 'name' => 'Template OS Template P',
+ 'template' => 'Template OS Template P',
'discovery_rules' => [
['key' => 'drule4', 'item_prototypes' => [['key' => 'item10']]]
]
@@ -414,7 +780,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template O',
+ 'template' => 'Template O',
'discovery_rules' => [
[
'key' => 'drule3',
@@ -427,7 +793,7 @@ class C52ImportConverterTest extends CImportConverterTest {
'uuid' => generateUuidV4('Template O')
],
[
- 'name' => 'Template OS Template P',
+ 'template' => 'Template OS Template P',
'discovery_rules' => [
[
'key' => 'drule4',
@@ -444,13 +810,13 @@ class C52ImportConverterTest extends CImportConverterTest {
];
}
- protected function getTriggerProtypeUuidData(): array {
+ protected function getTriggerPrototypeUuidData(): array {
// Use "discovery rule key/trigger prototype name/expanded expression/expanded recovery expression".
return [
[
'templates' => [
[
- 'name' => 'Template Q',
+ 'template' => 'Template Q',
'discovery_rules' => [
[
'key' => 'drule5',
@@ -471,7 +837,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template Q',
+ 'template' => 'Template Q',
'discovery_rules' => [
[
'key' => 'drule5',
@@ -504,13 +870,13 @@ class C52ImportConverterTest extends CImportConverterTest {
];
}
- protected function getGraphProtypeUuidData(): array {
+ protected function getGraphPrototypeUuidData(): array {
// Use "template name/discovery rule key/graph prototype name".
return [
[
'templates' => [
[
- 'name' => 'Template R',
+ 'template' => 'Template R',
'discovery_rules' => [
[
'key' => 'drule6',
@@ -521,7 +887,7 @@ class C52ImportConverterTest extends CImportConverterTest {
]
],
[
- 'name' => 'Template OS Template S',
+ 'template' => 'Template OS Template S',
'discovery_rules' => [
[
'key' => 'drule7',
@@ -536,7 +902,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template R',
+ 'template' => 'Template R',
'discovery_rules' => [
[
'key' => 'drule6',
@@ -549,7 +915,7 @@ class C52ImportConverterTest extends CImportConverterTest {
'uuid' => generateUuidV4('Template R')
],
[
- 'name' => 'Template OS Template S',
+ 'template' => 'Template OS Template S',
'discovery_rules' => [
[
'key' => 'drule7',
@@ -566,13 +932,13 @@ class C52ImportConverterTest extends CImportConverterTest {
];
}
- protected function getHosthProtypeUuidData(): array {
+ protected function getHostPrototypeUuidData(): array {
// Use "template name/discovery rule key/host prototype name".
return [
[
'templates' => [
[
- 'name' => 'Template T',
+ 'template' => 'Template T',
'discovery_rules' => [
[
'key' => 'drule8',
@@ -583,7 +949,7 @@ class C52ImportConverterTest extends CImportConverterTest {
]
],
[
- 'name' => 'Template OS Template T',
+ 'template' => 'Template OS Template T',
'discovery_rules' => [
[
'key' => 'drule9',
@@ -598,7 +964,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'templates' => [
[
- 'name' => 'Template T',
+ 'template' => 'Template T',
'discovery_rules' => [
[
'key' => 'drule8',
@@ -611,7 +977,7 @@ class C52ImportConverterTest extends CImportConverterTest {
'uuid' => generateUuidV4('Template T')
],
[
- 'name' => 'Template OS Template T',
+ 'template' => 'Template OS Template T',
'discovery_rules' => [
[
'key' => 'drule9',
@@ -634,7 +1000,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'hosts' => [
[
- 'name' => 'Host',
+ 'host' => 'Host',
'items' => [
[
'key' => 'item',
@@ -675,7 +1041,7 @@ class C52ImportConverterTest extends CImportConverterTest {
[
'hosts' => [
[
- 'name' => 'Host',
+ 'host' => 'Host',
'items' => [
[
'key' => 'item',
@@ -715,68 +1081,13 @@ class C52ImportConverterTest extends CImportConverterTest {
]
];
}
- /**
- * Testing data provider.
- *
- * @return array
- */
- public function dataProviderConvert(): array {
- return [
- [
- [],
- []
- ],
- $this->getTemplateNameUuidData(),
- $this->getGroupsUuidData(),
- $this->getTemplateItemsUuidData(),
- $this->getTriggerUuidData(),
- $this->getGraphUuidData(),
- $this->getTemplateDashboardUuidData(),
- $this->getHttptestUuidData(),
- $this->getValuemapsUuidData(),
- $this->getDiscoveryRuleNameUuidData(),
- $this->getItemProtypeUuidData(),
- $this->getTriggerProtypeUuidData(),
- $this->getGraphProtypeUuidData(),
- $this->getHosthProtypeUuidData(),
- $this->getHostUuidData()
- ];
- }
-
- /**
- * @dataProvider dataProviderConvert
- *
- * @param array $data
- * @param array $expected
- */
- public function testConvert(array $data, array $expected) {
- $this->assertConvert($this->createExpectedResult($expected), $this->createSource($data));
- }
-
- protected function createSource(array $data = []) {
- return [
- 'zabbix_export' => array_merge([
- 'version' => '5.2',
- 'date' => '2020-01-01T00:00:00Z'
- ], $data)
- ];
- }
-
- protected function createExpectedResult(array $data = []) {
- return [
- 'zabbix_export' => array_merge([
- 'version' => '5.4',
- 'date' => '2020-01-01T00:00:00Z'
- ], $data)
- ];
- }
- protected function assertConvert(array $expected, array $source) {
+ protected function assertConvert(array $expected, array $source): void {
$result = $this->createConverter()->convert($source);
$this->assertEquals($expected, $result);
}
- protected function createConverter() {
+ protected function createConverter(): C52ImportConverter {
return new C52ImportConverter();
}
}
diff --git a/ui/tests/unit/include/classes/import/converters/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/classes/import/converters/C52TriggerExpressionConverterTest.php
new file mode 100644
index 00000000000..57ccbfac9ea
--- /dev/null
+++ b/ui/tests/unit/include/classes/import/converters/C52TriggerExpressionConverterTest.php
@@ -0,0 +1,540 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class C52TriggerExpressionConverterTest extends TestCase {
+
+ /**
+ * @var C52TriggerExpressionConverter
+ */
+ private $converter;
+
+ protected function setUp(): void {
+ $this->converter = new C52TriggerExpressionConverter();
+ }
+
+ protected function tearDown(): void {
+ $this->converter = null;
+ }
+
+ public function simpleProviderData() {
+ return [
+ [
+ '{host:key.last(0)} or {host:key.last(10)}',
+ 'last(/host/key) or last(/host/key)'
+ ],
+ [
+ '{host:key.band(,9)}',
+ 'bitand(last(/host/key),9)'
+ ],
+ [
+ '{host:key.logeventid()} or {host:key.logeventid("")}',
+ 'logeventid(/host/key) or logeventid(/host/key,,"")'
+ ],
+ [
+ '{host:key.logsource()} or {host:key.logsource("")}',
+ 'logsource(/host/key) or logsource(/host/key,,"")'
+ ],
+ [
+ '{host:key.strlen()} or {host:key.strlen(123)} or {host:key.strlen("123")} or {host:key.strlen({$M})} or {host:key.strlen("{$M}")}',
+ 'length(last(/host/key)) or length(last(/host/key)) or length(last(/host/key)) or length(last(/host/key,{$M})) or length(last(/host/key,{$M}))'
+ ],
+ [
+ '{host:key.regexp("","")}',
+ 'find(/host/key,,"regexp","")'
+ ],
+ [
+ '{host:key.iregexp("","")}',
+ 'find(/host/key,,"iregexp","")'
+ ],
+ [
+ '{t:trap[{#IF}].last({#PERIOD},{#TIMESHIFT})}',
+ 'last(/t/trap[{#IF}],{#PERIOD}:now-{#TIMESHIFT})'
+ ],
+ [
+ '{t:trap[{#IF}].band({#PERIOD},123,{#TIMESHIFT})}',
+ 'bitand(last(/t/trap[{#IF}],{#PERIOD}:now-{#TIMESHIFT}),123)'
+ ],
+ [
+ '{t:trap[{#IF}].band("{{#PERIOD}.regsub(\"^[0-9]+\", \1)}",123,{#TIMESHIFT})}',
+ 'bitand(last(/t/trap[{#IF}],{{#PERIOD}.regsub("^[0-9]+", \1)}:now-{#TIMESHIFT}),123)'
+ ],
+ [
+ '{t:log.logeventid()} or {t:log.logeventid( )} or {t:log.logeventid( "" )} or {t:log.logeventid( " " )} or {t:log.logeventid( "pattern" )} or {t:log.logeventid(pattern)} or {t:log.logeventid( pattern)}',
+ 'logeventid(/t/log) or logeventid(/t/log) or logeventid(/t/log,,"") or logeventid(/t/log,," ") or logeventid(/t/log,,"pattern") or logeventid(/t/log,,"pattern") or logeventid(/t/log,,"pattern")'
+ ],
+ [
+ '{t:log.logseverity()} or {t:log.logseverity( abc)} or {t:log.logseverity( "" )} or {t:log.logseverity( " " )} or {t:log.logseverity( "abc" )}',
+ 'logseverity(/t/log) or logseverity(/t/log) or logseverity(/t/log) or logseverity(/t/log) or logseverity(/t/log)'
+ ],
+ [
+ '{t:log.logsource()} or {t:log.logsource( )} or {t:log.logsource( "" )} or {t:log.logsource( " " )} or {t:log.logsource( "pattern" )}',
+ 'logsource(/t/log) or logsource(/t/log) or logsource(/t/log,,"") or logsource(/t/log,," ") or logsource(/t/log,,"pattern")'
+ ],
+ [
+ '{t:str.iregexp(abc)} or {t:str.iregexp( abc)} or {t:str.iregexp("abc")} or {t:str.iregexp( "abc")} or {t:str.iregexp( "abc" )} or {t:str.iregexp(abc,)} or {t:str.iregexp( abc, )} or {t:str.iregexp("abc","")} or {t:str.iregexp( "abc", "")} or {t:str.iregexp( "abc" , "" )} or {t:str.iregexp(abc,#1)} or {t:str.iregexp( abc, #1)} or {t:str.iregexp("abc","#1")} or {t:str.iregexp( "abc", "#1")} or {t:str.iregexp( "abc" , "#1" )} or {t:str.iregexp(abc,5)} or {t:str.iregexp( abc, 5)} or {t:str.iregexp("abc","5")} or {t:str.iregexp( "abc", "5")} or {t:str.iregexp( "abc" , "5" )} or {t:str.iregexp(abc,10m)} or {t:str.iregexp( abc, 10m)} or {t:str.iregexp("abc","10m")} or {t:str.iregexp( "abc", "10m")} or {t:str.iregexp( "abc" , "10m" )} or {t:str.iregexp({$VAL},{$M: ctx})} or {t:str.iregexp( {$VAL}, {$M: ctx})} or {t:str.iregexp("{$VAL}","{$M: ctx}")} or {t:str.iregexp( "{$VAL}", "{$M: ctx}")} or {t:str.iregexp( "{$VAL}" , "{$M: ctx}" )}',
+ 'find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,,"iregexp","abc") or find(/t/str,#1,"iregexp","abc") or find(/t/str,#1,"iregexp","abc") or find(/t/str,#1,"iregexp","abc") or find(/t/str,#1,"iregexp","abc") or find(/t/str,#1,"iregexp","abc") or find(/t/str,5s,"iregexp","abc") or find(/t/str,5s,"iregexp","abc") or find(/t/str,5s,"iregexp","abc") or find(/t/str,5s,"iregexp","abc") or find(/t/str,5s,"iregexp","abc") or find(/t/str,10m,"iregexp","abc") or find(/t/str,10m,"iregexp","abc") or find(/t/str,10m,"iregexp","abc") or find(/t/str,10m,"iregexp","abc") or find(/t/str,10m,"iregexp","abc") or find(/t/str,{$M: ctx},"iregexp","{$VAL}") or find(/t/str,{$M: ctx},"iregexp","{$VAL}") or find(/t/str,{$M: ctx},"iregexp","{$VAL}") or find(/t/str,{$M: ctx},"iregexp","{$VAL}") or find(/t/str,{$M: ctx},"iregexp","{$VAL}")'
+ ],
+ [
+ '{t:str.regexp(abc)} or {t:str.regexp( abc)} or {t:str.regexp("abc")} or {t:str.regexp( "abc")} or {t:str.regexp( "abc" )} or {t:str.regexp(abc,)} or {t:str.regexp( abc, )} or {t:str.regexp("abc","")} or {t:str.regexp( "abc", "")} or {t:str.regexp( "abc" , "" )} or {t:str.regexp(abc,#1)} or {t:str.regexp( abc, #1)} or {t:str.regexp("abc","#1")} or {t:str.regexp( "abc", "#1")} or {t:str.regexp( "abc" , "#1" )} or {t:str.regexp(abc,5)} or {t:str.regexp( abc, 5)} or {t:str.regexp("abc","5")} or {t:str.regexp( "abc", "5")} or {t:str.regexp( "abc" , "5" )} or {t:str.regexp(abc,10m)} or {t:str.regexp( abc, 10m)} or {t:str.regexp("abc","10m")} or {t:str.regexp( "abc", "10m")} or {t:str.regexp( "abc" , "10m" )} or {t:str.regexp({$VAL},{$M: ctx})} or {t:str.regexp( {$VAL}, {$M: ctx})} or {t:str.regexp("{$VAL}","{$M: ctx}")} or {t:str.regexp( "{$VAL}", "{$M: ctx}")} or {t:str.regexp( "{$VAL}" , "{$M: ctx}" )}',
+ 'find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,,"regexp","abc") or find(/t/str,#1,"regexp","abc") or find(/t/str,#1,"regexp","abc") or find(/t/str,#1,"regexp","abc") or find(/t/str,#1,"regexp","abc") or find(/t/str,#1,"regexp","abc") or find(/t/str,5s,"regexp","abc") or find(/t/str,5s,"regexp","abc") or find(/t/str,5s,"regexp","abc") or find(/t/str,5s,"regexp","abc") or find(/t/str,5s,"regexp","abc") or find(/t/str,10m,"regexp","abc") or find(/t/str,10m,"regexp","abc") or find(/t/str,10m,"regexp","abc") or find(/t/str,10m,"regexp","abc") or find(/t/str,10m,"regexp","abc") or find(/t/str,{$M: ctx},"regexp","{$VAL}") or find(/t/str,{$M: ctx},"regexp","{$VAL}") or find(/t/str,{$M: ctx},"regexp","{$VAL}") or find(/t/str,{$M: ctx},"regexp","{$VAL}") or find(/t/str,{$M: ctx},"regexp","{$VAL}")'
+ ],
+ [
+ '{t:str.str(abc)} or {t:str.str( abc)} or {t:str.str("abc")} or {t:str.str( "abc")} or {t:str.str( "abc" )} or {t:str.str(abc,)} or {t:str.str( abc, )} or {t:str.str("abc","")} or {t:str.str( "abc", "")} or {t:str.str( "abc" , "" )} or {t:str.str(abc,#1)} or {t:str.str( abc, #1)} or {t:str.str("abc","#1")} or {t:str.str( "abc", "#1")} or {t:str.str( "abc" , "#1" )} or {t:str.str(abc,5)} or {t:str.str( abc, 5)} or {t:str.str("abc","5")} or {t:str.str( "abc", "5")} or {t:str.str( "abc" , "5" )} or {t:str.str(abc,10m)} or {t:str.str( abc, 10m)} or {t:str.str("abc","10m")} or {t:str.str( "abc", "10m")} or {t:str.str( "abc" , "10m" )} or {t:str.str({$VAL},{$M: ctx})} or {t:str.str( {$VAL}, {$M: ctx})} or {t:str.str("{$VAL}","{$M: ctx}")} or {t:str.str( "{$VAL}", "{$M: ctx}")} or {t:str.str( "{$VAL}" , "{$M: ctx}" )}',
+ 'find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,,"like","abc") or find(/t/str,#1,"like","abc") or find(/t/str,#1,"like","abc") or find(/t/str,#1,"like","abc") or find(/t/str,#1,"like","abc") or find(/t/str,#1,"like","abc") or find(/t/str,5s,"like","abc") or find(/t/str,5s,"like","abc") or find(/t/str,5s,"like","abc") or find(/t/str,5s,"like","abc") or find(/t/str,5s,"like","abc") or find(/t/str,10m,"like","abc") or find(/t/str,10m,"like","abc") or find(/t/str,10m,"like","abc") or find(/t/str,10m,"like","abc") or find(/t/str,10m,"like","abc") or find(/t/str,{$M: ctx},"like","{$VAL}") or find(/t/str,{$M: ctx},"like","{$VAL}") or find(/t/str,{$M: ctx},"like","{$VAL}") or find(/t/str,{$M: ctx},"like","{$VAL}") or find(/t/str,{$M: ctx},"like","{$VAL}")'
+ ],
+ [
+ '{t:str.strlen()} or {t:str.strlen( )} or {t:str.strlen("")} or {t:str.strlen( "")} or {t:str.strlen( "" )} or {t:str.strlen(#1)} or {t:str.strlen( #1)} or {t:str.strlen("#1")} or {t:str.strlen( "#1")} or {t:str.strlen( "#1" )} or {t:str.strlen(5)} or {t:str.strlen( 5)} or {t:str.strlen("5")} or {t:str.strlen( "5")} or {t:str.strlen( "5" )} or {t:str.strlen(10m)} or {t:str.strlen( 10m)} or {t:str.strlen("10m")} or {t:str.strlen( "10m")} or {t:str.strlen( "10m" )} or {t:str.strlen(10m,1h)} or {t:str.strlen(10m, 1h)} or {t:str.strlen(10m,"1h")} or {t:str.strlen(10m, "1h")} or {t:str.strlen(10m, "1h" )} or {t:str.strlen({$PERIOD},{$TIMESHIFT})} or {t:str.strlen( {$PERIOD}, {$TIMESHIFT})} or {t:str.strlen("{$PERIOD}","{$TIMESHIFT}")} or {t:str.strlen( "{$PERIOD}", "{$TIMESHIFT}")} or {t:str.strlen( "{$PERIOD}" , "{$TIMESHIFT}" )}',
+ 'length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str,#1)) or length(last(/t/str,#1)) or length(last(/t/str,#1)) or length(last(/t/str,#1)) or length(last(/t/str,#1)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str)) or length(last(/t/str,#1:now-1h)) or length(last(/t/str,#1:now-1h)) or length(last(/t/str,#1:now-1h)) or length(last(/t/str,#1:now-1h)) or length(last(/t/str,#1:now-1h)) or length(last(/t/str,{$PERIOD}:now-{$TIMESHIFT})) or length(last(/t/str,{$PERIOD}:now-{$TIMESHIFT})) or length(last(/t/str,{$PERIOD}:now-{$TIMESHIFT})) or length(last(/t/str,{$PERIOD}:now-{$TIMESHIFT})) or length(last(/t/str,{$PERIOD}:now-{$TIMESHIFT}))'
+ ],
+ [
+ '{t:uint64.abschange()} or {t:uint64.abschange( )} or {t:uint64.abschange( "" )} or {t:uint64.abschange( " " )} or {t:uint64.abschange( "text" )}',
+ 'abs(change(/t/uint64)) or abs(change(/t/uint64)) or abs(change(/t/uint64)) or abs(change(/t/uint64)) or abs(change(/t/uint64))'
+ ],
+ [
+ '{t:uint64.avg(#1)} or {t:uint64.avg( #1)} or {t:uint64.avg("#1")} or {t:uint64.avg( "#1")} or {t:uint64.avg( "#1" )} or {t:uint64.avg(5)} or {t:uint64.avg( 5)} or {t:uint64.avg("5")} or {t:uint64.avg( "5")} or {t:uint64.avg( "5" )} or {t:uint64.avg(10m)} or {t:uint64.avg( 10m)} or {t:uint64.avg("10m")} or {t:uint64.avg( "10m")} or {t:uint64.avg( "10m" )} or {t:uint64.avg({$M: ctx})} or {t:uint64.avg( {$M: ctx})} or {t:uint64.avg("{$M: ctx}")} or {t:uint64.avg( "{$M: ctx}")} or {t:uint64.avg( "{$M: ctx}" )} or {t:uint64.avg(#1,)} or {t:uint64.avg(#1,3600)} or {t:uint64.avg(#1, 3600)} or {t:uint64.avg(#1,"3600")} or {t:uint64.avg(#1, "3600")} or {t:uint64.avg(#1, "3600" )} or {t:uint64.avg(#1,1h)} or {t:uint64.avg(#1, 1h)} or {t:uint64.avg(#1,"1h")} or {t:uint64.avg(#1, "1h")} or {t:uint64.avg(#1, "1h" )} or {t:uint64.avg(#1,{$M: ctx})} or {t:uint64.avg(#1, {$M: ctx})} or {t:uint64.avg(#1,"{$M: ctx}")} or {t:uint64.avg(#1, "{$M: ctx}")} or {t:uint64.avg(#1, "{$M: ctx}" )}',
+ 'avg(/t/uint64,#1) or avg(/t/uint64,#1) or avg(/t/uint64,#1) or avg(/t/uint64,#1) or avg(/t/uint64,#1) or avg(/t/uint64,5s) or avg(/t/uint64,5s) or avg(/t/uint64,5s) or avg(/t/uint64,5s) or avg(/t/uint64,5s) or avg(/t/uint64,10m) or avg(/t/uint64,10m) or avg(/t/uint64,10m) or avg(/t/uint64,10m) or avg(/t/uint64,10m) or avg(/t/uint64,{$M: ctx}) or avg(/t/uint64,{$M: ctx}) or avg(/t/uint64,{$M: ctx}) or avg(/t/uint64,{$M: ctx}) or avg(/t/uint64,{$M: ctx}) or avg(/t/uint64,#1) or avg(/t/uint64,#1:now-3600s) or avg(/t/uint64,#1:now-3600s) or avg(/t/uint64,#1:now-3600s) or avg(/t/uint64,#1:now-3600s) or avg(/t/uint64,#1:now-3600s) or avg(/t/uint64,#1:now-1h) or avg(/t/uint64,#1:now-1h) or avg(/t/uint64,#1:now-1h) or avg(/t/uint64,#1:now-1h) or avg(/t/uint64,#1:now-1h) or avg(/t/uint64,#1:now-{$M: ctx}) or avg(/t/uint64,#1:now-{$M: ctx}) or avg(/t/uint64,#1:now-{$M: ctx}) or avg(/t/uint64,#1:now-{$M: ctx}) or avg(/t/uint64,#1:now-{$M: ctx})'
+ ],
+ [
+ '{t:uint64.band(#1,256)} or {t:uint64.band( #1, 256)} or {t:uint64.band("#1","256")} or {t:uint64.band( "#1", "256")} or {t:uint64.band( "#1" , "256" )} or {t:uint64.band(5,{$M: ctx})} or {t:uint64.band( 5, {$M: ctx})} or {t:uint64.band("5","{$M: ctx}")} or {t:uint64.band( "5", "{$M: ctx}")} or {t:uint64.band( "5" , "{$M: ctx}" )} or {t:uint64.band(10m,1024)} or {t:uint64.band( 10m, 1024)} or {t:uint64.band("10m","1024")} or {t:uint64.band( "10m", "1024")} or {t:uint64.band( "10m" , "1024" )} or {t:uint64.band({$M: ctx},18446744073709551615)} or {t:uint64.band( {$M: ctx}, 18446744073709551615)} or {t:uint64.band("{$M: ctx}","18446744073709551615")} or {t:uint64.band( "{$M: ctx}", "18446744073709551615")} or {t:uint64.band( "{$M: ctx}" , "18446744073709551615" )} or {t:uint64.band(#1,256,)} or {t:uint64.band(#1,1,3600)} or {t:uint64.band(#1,1, 3600)} or {t:uint64.band(#1,1,"3600")} or {t:uint64.band(#1,1, "3600")} or {t:uint64.band(#1,1, "3600" )} or {t:uint64.band(#1,1,1h)} or {t:uint64.band(#1,1, 1h)} or {t:uint64.band(#1,1,"1h")} or {t:uint64.band(#1,1, "1h")} or {t:uint64.band(#1,1, "1h" )} or {t:uint64.band(#1,1,{$M: ctx})} or {t:uint64.band(#1,1, {$M: ctx})} or {t:uint64.band(#1,1,"{$M: ctx}")} or {t:uint64.band(#1,1, "{$M: ctx}")} or {t:uint64.band(#1,1, "{$M: ctx}" )}',
+ 'bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64),{$M: ctx}) or bitand(last(/t/uint64),{$M: ctx}) or bitand(last(/t/uint64),{$M: ctx}) or bitand(last(/t/uint64),{$M: ctx}) or bitand(last(/t/uint64),{$M: ctx}) or bitand(last(/t/uint64),1024) or bitand(last(/t/uint64),1024) or bitand(last(/t/uint64),1024) or bitand(last(/t/uint64),1024) or bitand(last(/t/uint64),1024) or bitand(last(/t/uint64,{$M: ctx}),18446744073709551615) or bitand(last(/t/uint64,{$M: ctx}),18446744073709551615) or bitand(last(/t/uint64,{$M: ctx}),18446744073709551615) or bitand(last(/t/uint64,{$M: ctx}),18446744073709551615) or bitand(last(/t/uint64,{$M: ctx}),18446744073709551615) or bitand(last(/t/uint64,#1),256) or bitand(last(/t/uint64,#1:now-3600s),1) or bitand(last(/t/uint64,#1:now-3600s),1) or bitand(last(/t/uint64,#1:now-3600s),1) or bitand(last(/t/uint64,#1:now-3600s),1) or bitand(last(/t/uint64,#1:now-3600s),1) or bitand(last(/t/uint64,#1:now-1h),1) or bitand(last(/t/uint64,#1:now-1h),1) or bitand(last(/t/uint64,#1:now-1h),1) or bitand(last(/t/uint64,#1:now-1h),1) or bitand(last(/t/uint64,#1:now-1h),1) or bitand(last(/t/uint64,#1:now-{$M: ctx}),1) or bitand(last(/t/uint64,#1:now-{$M: ctx}),1) or bitand(last(/t/uint64,#1:now-{$M: ctx}),1) or bitand(last(/t/uint64,#1:now-{$M: ctx}),1) or bitand(last(/t/uint64,#1:now-{$M: ctx}),1)'
+ ],
+ [
+ '{t:uint64.change()} or {t:uint64.change( )} or {t:uint64.change( "" )} or {t:uint64.change( " " )} or {t:uint64.change( "text" )} or {t:uint64.change(text)} or {t:uint64.change( text)}',
+ 'change(/t/uint64) or change(/t/uint64) or change(/t/uint64) or change(/t/uint64) or change(/t/uint64) or change(/t/uint64) or change(/t/uint64)'
+ ],
+ [
+ '{t:uint64.count(#1,256)} or {t:uint64.count( #1, 256)} or {t:uint64.count("#1","256")} or {t:uint64.count( "#1", "256")} or {t:uint64.count( "#1" , "256" )} or {t:uint64.count(5,{$M: ctx})} or {t:uint64.count( 5, {$M: ctx})} or {t:uint64.count("5","{$M: ctx}")} or {t:uint64.count( "5", "{$M: ctx}")} or {t:uint64.count( "5" , "{$M: ctx}" )} or {t:uint64.count(10m,abc)} or {t:uint64.count( 10m, abc)} or {t:uint64.count("10m","abc")} or {t:uint64.count( "10m", "abc")} or {t:uint64.count( "10m" , "abc" )} or {t:uint64.count({$M: ctx},18446744073709551615)} or {t:uint64.count( {$M: ctx}, 18446744073709551615)} or {t:uint64.count("{$M: ctx}","18446744073709551615")} or {t:uint64.count( "{$M: ctx}", "18446744073709551615")} or {t:uint64.count( "{$M: ctx}" , "18446744073709551615" )} or {t:uint64.count(#1,256,)} or {t:uint64.count(#1,1,eq,3600)} or {t:uint64.count(#1,1, ne, 3600)} or {t:uint64.count(#1,1,"gt","3600")} or {t:uint64.count(#1,1, "ge", "3600")} or {t:uint64.count(#1,1, "lt" , "3600" )} or {t:uint64.count(#1,1,le,1h)} or {t:uint64.count(#1,1, like, 1h)} or {t:uint64.count(#1,1,"band","1h")} or {t:uint64.count(#1,1, "regexp", "1h")} or {t:uint64.count(#1,1, "iregexp" , "1h" )} or {t:uint64.count(#1,1,eq,{$M: ctx})} or {t:uint64.count(#1,1,eq, {$M: ctx})} or {t:uint64.count(#1,1,eq,"{$M: ctx}")} or {t:uint64.count(#1,1,eq, "{$M: ctx}")} or {t:uint64.count(#1,1,eq, "{$M: ctx}" )} or {t:uint64.count(#1,256,,)} or {t:uint64.count(#1,256,,1d)} or {t:uint64.count(#5,256,,1d)} or {t:uint64.count({$NUM},256,,{$TIMESHIFT})}',
+ 'count(/t/uint64,#1,,"256") or count(/t/uint64,#1,,"256") or count(/t/uint64,#1,,"256") or count(/t/uint64,#1,,"256") or count(/t/uint64,#1,,"256") or count(/t/uint64,5s,,"{$M: ctx}") or count(/t/uint64,5s,,"{$M: ctx}") or count(/t/uint64,5s,,"{$M: ctx}") or count(/t/uint64,5s,,"{$M: ctx}") or count(/t/uint64,5s,,"{$M: ctx}") or count(/t/uint64,10m,,"abc") or count(/t/uint64,10m,,"abc") or count(/t/uint64,10m,,"abc") or count(/t/uint64,10m,,"abc") or count(/t/uint64,10m,,"abc") or count(/t/uint64,{$M: ctx},,"18446744073709551615") or count(/t/uint64,{$M: ctx},,"18446744073709551615") or count(/t/uint64,{$M: ctx},,"18446744073709551615") or count(/t/uint64,{$M: ctx},,"18446744073709551615") or count(/t/uint64,{$M: ctx},,"18446744073709551615") or count(/t/uint64,#1,,"256") or count(/t/uint64,#1:now-3600s,"eq","1") or count(/t/uint64,#1:now-3600s,"ne","1") or count(/t/uint64,#1:now-3600s,"gt","1") or count(/t/uint64,#1:now-3600s,"ge","1") or count(/t/uint64,#1:now-3600s,"lt","1") or count(/t/uint64,#1:now-1h,"le","1") or count(/t/uint64,#1:now-1h,"like","1") or count(/t/uint64,#1:now-1h,"bitand","1") or count(/t/uint64,#1:now-1h,"regexp","1") or count(/t/uint64,#1:now-1h,"iregexp","1") or count(/t/uint64,#1:now-{$M: ctx},"eq","1") or count(/t/uint64,#1:now-{$M: ctx},"eq","1") or count(/t/uint64,#1:now-{$M: ctx},"eq","1") or count(/t/uint64,#1:now-{$M: ctx},"eq","1") or count(/t/uint64,#1:now-{$M: ctx},"eq","1") or count(/t/uint64,#1,,"256") or count(/t/uint64,#1:now-1d,,"256") or count(/t/uint64,#5:now-1d,,"256") or count(/t/uint64,{$NUM}:now-{$TIMESHIFT},,"256")'
+ ],
+ [
+ '{t:uint64.date()} or {t:uint64.date( )} or {t:uint64.date( "" )} or {t:uint64.date( " " )} or {t:uint64.date( "text" )}',
+ '(date() or date() or date() or date() or date()) or (last(/t/uint64)<>last(/t/uint64))'
+ ],
+ [
+ '{t:uint64.dayofmonth()} or {t:uint64.dayofmonth( )} or {t:uint64.dayofmonth( "" )} or {t:uint64.dayofmonth( " " )} or {t:uint64.dayofmonth( "text" )}',
+ '(dayofmonth() or dayofmonth() or dayofmonth() or dayofmonth() or dayofmonth()) or (last(/t/uint64)<>last(/t/uint64))'
+ ],
+ [
+ '{t:uint64.dayofweek()} or {t:uint64.dayofweek( )} or {t:uint64.dayofweek( "" )} or {t:uint64.dayofweek( " " )} or {t:uint64.dayofweek( "text" )}',
+ '(dayofweek() or dayofweek() or dayofweek() or dayofweek() or dayofweek()) or (last(/t/uint64)<>last(/t/uint64))'
+ ],
+ [
+ '{t:uint64.delta(#1)} or {t:uint64.delta( #1)} or {t:uint64.delta("#1")} or {t:uint64.delta( "#1")} or {t:uint64.delta( "#1" )} or {t:uint64.delta(5)} or {t:uint64.delta( 5)} or {t:uint64.delta("5")} or {t:uint64.delta( "5")} or {t:uint64.delta( "5" )} or {t:uint64.delta(10m)} or {t:uint64.delta( 10m)} or {t:uint64.delta("10m")} or {t:uint64.delta( "10m")} or {t:uint64.delta( "10m" )} or {t:uint64.delta({$M: ctx})} or {t:uint64.delta( {$M: ctx})} or {t:uint64.delta("{$M: ctx}")} or {t:uint64.delta( "{$M: ctx}")} or {t:uint64.delta( "{$M: ctx}" )} or {t:uint64.delta(#1,)} or {t:uint64.delta(#1,3600)} or {t:uint64.delta(#1, 3600)} or {t:uint64.delta(#1,"3600")} or {t:uint64.delta(#1, "3600")} or {t:uint64.delta(#1, "3600" )} or {t:uint64.delta(#1,1h)} or {t:uint64.delta(#1, 1h)} or {t:uint64.delta(#1,"1h")} or {t:uint64.delta(#1, "1h")} or {t:uint64.delta(#1, "1h" )} or {t:uint64.delta(#1,{$M: ctx})} or {t:uint64.delta(#1, {$M: ctx})} or {t:uint64.delta(#1,"{$M: ctx}")} or {t:uint64.delta(#1, "{$M: ctx}")} or {t:uint64.delta(#1, "{$M: ctx}" )}',
+ '(max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,5s)-min(/t/uint64,5s)) or (max(/t/uint64,5s)-min(/t/uint64,5s)) or (max(/t/uint64,5s)-min(/t/uint64,5s)) or (max(/t/uint64,5s)-min(/t/uint64,5s)) or (max(/t/uint64,5s)-min(/t/uint64,5s)) or (max(/t/uint64,10m)-min(/t/uint64,10m)) or (max(/t/uint64,10m)-min(/t/uint64,10m)) or (max(/t/uint64,10m)-min(/t/uint64,10m)) or (max(/t/uint64,10m)-min(/t/uint64,10m)) or (max(/t/uint64,10m)-min(/t/uint64,10m)) or (max(/t/uint64,{$M: ctx})-min(/t/uint64,{$M: ctx})) or (max(/t/uint64,{$M: ctx})-min(/t/uint64,{$M: ctx})) or (max(/t/uint64,{$M: ctx})-min(/t/uint64,{$M: ctx})) or (max(/t/uint64,{$M: ctx})-min(/t/uint64,{$M: ctx})) or (max(/t/uint64,{$M: ctx})-min(/t/uint64,{$M: ctx})) or (max(/t/uint64,#1)-min(/t/uint64,#1)) or (max(/t/uint64,#1:now-3600s)-min(/t/uint64,#1:now-3600s)) or (max(/t/uint64,#1:now-3600s)-min(/t/uint64,#1:now-3600s)) or (max(/t/uint64,#1:now-3600s)-min(/t/uint64,#1:now-3600s)) or (max(/t/uint64,#1:now-3600s)-min(/t/uint64,#1:now-3600s)) or (max(/t/uint64,#1:now-3600s)-min(/t/uint64,#1:now-3600s)) or (max(/t/uint64,#1:now-1h)-min(/t/uint64,#1:now-1h)) or (max(/t/uint64,#1:now-1h)-min(/t/uint64,#1:now-1h)) or (max(/t/uint64,#1:now-1h)-min(/t/uint64,#1:now-1h)) or (max(/t/uint64,#1:now-1h)-min(/t/uint64,#1:now-1h)) or (max(/t/uint64,#1:now-1h)-min(/t/uint64,#1:now-1h)) or (max(/t/uint64,#1:now-{$M: ctx})-min(/t/uint64,#1:now-{$M: ctx})) or (max(/t/uint64,#1:now-{$M: ctx})-min(/t/uint64,#1:now-{$M: ctx})) or (max(/t/uint64,#1:now-{$M: ctx})-min(/t/uint64,#1:now-{$M: ctx})) or (max(/t/uint64,#1:now-{$M: ctx})-min(/t/uint64,#1:now-{$M: ctx})) or (max(/t/uint64,#1:now-{$M: ctx})-min(/t/uint64,#1:now-{$M: ctx}))'
+ ],
+ [
+ '{t:uint64.diff()} or {t:uint64.diff( )} or {t:uint64.diff( "" )} or {t:uint64.diff( " " )} or {t:uint64.diff( "text" )}',
+ '(last(/t/uint64,#1)<>last(/t/uint64,#2)) or (last(/t/uint64,#1)<>last(/t/uint64,#2)) or (last(/t/uint64,#1)<>last(/t/uint64,#2)) or (last(/t/uint64,#1)<>last(/t/uint64,#2)) or (last(/t/uint64,#1)<>last(/t/uint64,#2))'
+ ],
+ [
+ '{t:uint64.forecast(#1,,1w)} or {t:uint64.forecast( #1, , 1w)} or {t:uint64.forecast("#1","","1w")} or {t:uint64.forecast( "#1", "", "1w")} or {t:uint64.forecast( "#1" , "" , "1w" )} or {t:uint64.forecast(5,,86400)} or {t:uint64.forecast( 5, , 86400)} or {t:uint64.forecast("5","","86400")} or {t:uint64.forecast( "5", "", "86400")} or {t:uint64.forecast( "5" , "" , "86400" )} or {t:uint64.forecast(10m,,{$M: ctx})} or {t:uint64.forecast( 10m, , {$M: ctx})} or {t:uint64.forecast("10m","","{$M: ctx}")} or {t:uint64.forecast( "10m", "", "{$M: ctx}")} or {t:uint64.forecast( "10m" , "" , "{$M: ctx}" )} or {t:uint64.forecast({$M: ctx},,1d,polynomial1,value)} or {t:uint64.forecast( {$M: ctx},,1d, polynomial2,avg)} or {t:uint64.forecast("{$M: ctx}",,1d,"polynomial3",max)} or {t:uint64.forecast( "{$M: ctx}",,1d, "polynomial4",min)} or {t:uint64.forecast( "{$M: ctx}" ,,1d, "polynomial5" ,delta)} or {t:uint64.forecast(#1,,30d,,)} or {t:uint64.forecast(#1,3600,1d,polynomial6)} or {t:uint64.forecast(#1, 3600,1d, linear)} or {t:uint64.forecast(#1,"3600",1d,"exponential")} or {t:uint64.forecast(#1, "3600",1d, "logarithmic")} or {t:uint64.forecast(#1, "3600" ,1d, "power" )} or {t:uint64.forecast(#1,1h,1d)} or {t:uint64.forecast(#1, 1h,1d)} or {t:uint64.forecast(#1,"1h",1d)} or {t:uint64.forecast(#1, "1h",1d)} or {t:uint64.forecast(#1, "1h" ,1d)} or {t:uint64.forecast(#1,{$M: ctx},1d)} or {t:uint64.forecast(#1, {$M: ctx},1d)} or {t:uint64.forecast(#1,"{$M: ctx}",1d)} or {t:uint64.forecast(#1, "{$M: ctx}",1d)} or {t:uint64.forecast(#1, "{$M: ctx}" ,1d)}',
+ 'forecast(/t/uint64,#1,1w) or forecast(/t/uint64,#1,1w) or forecast(/t/uint64,#1,1w) or forecast(/t/uint64,#1,1w) or forecast(/t/uint64,#1,1w) or forecast(/t/uint64,5s,86400s) or forecast(/t/uint64,5s,86400s) or forecast(/t/uint64,5s,86400s) or forecast(/t/uint64,5s,86400s) or forecast(/t/uint64,5s,86400s) or forecast(/t/uint64,10m,{$M: ctx}) or forecast(/t/uint64,10m,{$M: ctx}) or forecast(/t/uint64,10m,{$M: ctx}) or forecast(/t/uint64,10m,{$M: ctx}) or forecast(/t/uint64,10m,{$M: ctx}) or forecast(/t/uint64,{$M: ctx},1d,"polynomial1","value") or forecast(/t/uint64,{$M: ctx},1d,"polynomial2","avg") or forecast(/t/uint64,{$M: ctx},1d,"polynomial3","max") or forecast(/t/uint64,{$M: ctx},1d,"polynomial4","min") or forecast(/t/uint64,{$M: ctx},1d,"polynomial5","delta") or forecast(/t/uint64,#1,30d) or forecast(/t/uint64,#1:now-3600s,1d,"polynomial6") or forecast(/t/uint64,#1:now-3600s,1d,"linear") or forecast(/t/uint64,#1:now-3600s,1d,"exponential") or forecast(/t/uint64,#1:now-3600s,1d,"logarithmic") or forecast(/t/uint64,#1:now-3600s,1d,"power") or forecast(/t/uint64,#1:now-1h,1d) or forecast(/t/uint64,#1:now-1h,1d) or forecast(/t/uint64,#1:now-1h,1d) or forecast(/t/uint64,#1:now-1h,1d) or forecast(/t/uint64,#1:now-1h,1d) or forecast(/t/uint64,#1:now-{$M: ctx},1d) or forecast(/t/uint64,#1:now-{$M: ctx},1d) or forecast(/t/uint64,#1:now-{$M: ctx},1d) or forecast(/t/uint64,#1:now-{$M: ctx},1d) or forecast(/t/uint64,#1:now-{$M: ctx},1d)'
+ ],
+ [
+ '{t:uint64.fuzzytime(3600)} or {t:uint64.fuzzytime( 1h)} or {t:uint64.fuzzytime("24h")} or {t:uint64.fuzzytime( "1d")} or {t:uint64.fuzzytime( "1w" )}',
+ 'fuzzytime(/t/uint64,3600s) or fuzzytime(/t/uint64,1h) or fuzzytime(/t/uint64,24h) or fuzzytime(/t/uint64,1d) or fuzzytime(/t/uint64,1w)'
+ ],
+ [
+ '{t:uint64.last(#1)} or {t:uint64.last( #1)} or {t:uint64.last("#1")} or {t:uint64.last( "#1")} or {t:uint64.last( "#1" )} or {t:uint64.last(5)} or {t:uint64.last( 5)} or {t:uint64.last("5")} or {t:uint64.last( "5")} or {t:uint64.last( "5" )} or {t:uint64.last(10m)} or {t:uint64.last( 10m)} or {t:uint64.last("10m")} or {t:uint64.last( "10m")} or {t:uint64.last( "10m" )} or {t:uint64.last({$M: ctx})} or {t:uint64.last( {$M: ctx})} or {t:uint64.last("{$M: ctx}")} or {t:uint64.last( "{$M: ctx}")} or {t:uint64.last( "{$M: ctx}" )} or {t:uint64.last(#1,)} or {t:uint64.last(#1,3600)} or {t:uint64.last(#1, 3600)} or {t:uint64.last(#1,"3600")} or {t:uint64.last(#1, "3600")} or {t:uint64.last(#1, "3600" )} or {t:uint64.last(#1,1h)} or {t:uint64.last(#1, 1h)} or {t:uint64.last(#1,"1h")} or {t:uint64.last(#1, "1h")} or {t:uint64.last(#1, "1h" )} or {t:uint64.last(#1,{$M: ctx})} or {t:uint64.last(#1, {$M: ctx})} or {t:uint64.last(#1,"{$M: ctx}")} or {t:uint64.last(#1, "{$M: ctx}")} or {t:uint64.last(#1, "{$M: ctx}" )} or {t:uint64.last(,)} or {t:uint64.last(,1d)} or {t:uint64.last(,{$TIMESHIFT})} or {t:uint64.last(#5,)} or {t:uint64.last(#5,1d)} or {t:uint64.last(#5,{$TIMESHIFT})} or {t:uint64.last({$PERIOD},{$TIMESHIFT})}',
+ 'last(/t/uint64,#1) or last(/t/uint64,#1) or last(/t/uint64,#1) or last(/t/uint64,#1) or last(/t/uint64,#1) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64) or last(/t/uint64,{$M: ctx}) or last(/t/uint64,{$M: ctx}) or last(/t/uint64,{$M: ctx}) or last(/t/uint64,{$M: ctx}) or last(/t/uint64,{$M: ctx}) or last(/t/uint64,#1) or last(/t/uint64,#1:now-3600s) or last(/t/uint64,#1:now-3600s) or last(/t/uint64,#1:now-3600s) or last(/t/uint64,#1:now-3600s) or last(/t/uint64,#1:now-3600s) or last(/t/uint64,#1:now-1h) or last(/t/uint64,#1:now-1h) or last(/t/uint64,#1:now-1h) or last(/t/uint64,#1:now-1h) or last(/t/uint64,#1:now-1h) or last(/t/uint64,#1:now-{$M: ctx}) or last(/t/uint64,#1:now-{$M: ctx}) or last(/t/uint64,#1:now-{$M: ctx}) or last(/t/uint64,#1:now-{$M: ctx}) or last(/t/uint64,#1:now-{$M: ctx}) or last(/t/uint64) or last(/t/uint64,#1:now-1d) or last(/t/uint64,#1:now-{$TIMESHIFT}) or last(/t/uint64,#5) or last(/t/uint64,#5:now-1d) or last(/t/uint64,#5:now-{$TIMESHIFT}) or last(/t/uint64,{$PERIOD}:now-{$TIMESHIFT})'
+ ],
+ [
+ '{t:uint64.max(#1)} or {t:uint64.max( #1)} or {t:uint64.max("#1")} or {t:uint64.max( "#1")} or {t:uint64.max( "#1" )} or {t:uint64.max(5)} or {t:uint64.max( 5)} or {t:uint64.max("5")} or {t:uint64.max( "5")} or {t:uint64.max( "5" )} or {t:uint64.max(10m)} or {t:uint64.max( 10m)} or {t:uint64.max("10m")} or {t:uint64.max( "10m")} or {t:uint64.max( "10m" )} or {t:uint64.max({$M: ctx})} or {t:uint64.max( {$M: ctx})} or {t:uint64.max("{$M: ctx}")} or {t:uint64.max( "{$M: ctx}")} or {t:uint64.max( "{$M: ctx}" )} or {t:uint64.max(#1,)} or {t:uint64.max(#1,3600)} or {t:uint64.max(#1, 3600)} or {t:uint64.max(#1,"3600")} or {t:uint64.max(#1, "3600")} or {t:uint64.max(#1, "3600" )} or {t:uint64.max(#1,1h)} or {t:uint64.max(#1, 1h)} or {t:uint64.max(#1,"1h")} or {t:uint64.max(#1, "1h")} or {t:uint64.max(#1, "1h" )} or {t:uint64.max(#1,{$M: ctx})} or {t:uint64.max(#1, {$M: ctx})} or {t:uint64.max(#1,"{$M: ctx}")} or {t:uint64.max(#1, "{$M: ctx}")} or {t:uint64.max(#1, "{$M: ctx}" )}',
+ 'max(/t/uint64,#1) or max(/t/uint64,#1) or max(/t/uint64,#1) or max(/t/uint64,#1) or max(/t/uint64,#1) or max(/t/uint64,5s) or max(/t/uint64,5s) or max(/t/uint64,5s) or max(/t/uint64,5s) or max(/t/uint64,5s) or max(/t/uint64,10m) or max(/t/uint64,10m) or max(/t/uint64,10m) or max(/t/uint64,10m) or max(/t/uint64,10m) or max(/t/uint64,{$M: ctx}) or max(/t/uint64,{$M: ctx}) or max(/t/uint64,{$M: ctx}) or max(/t/uint64,{$M: ctx}) or max(/t/uint64,{$M: ctx}) or max(/t/uint64,#1) or max(/t/uint64,#1:now-3600s) or max(/t/uint64,#1:now-3600s) or max(/t/uint64,#1:now-3600s) or max(/t/uint64,#1:now-3600s) or max(/t/uint64,#1:now-3600s) or max(/t/uint64,#1:now-1h) or max(/t/uint64,#1:now-1h) or max(/t/uint64,#1:now-1h) or max(/t/uint64,#1:now-1h) or max(/t/uint64,#1:now-1h) or max(/t/uint64,#1:now-{$M: ctx}) or max(/t/uint64,#1:now-{$M: ctx}) or max(/t/uint64,#1:now-{$M: ctx}) or max(/t/uint64,#1:now-{$M: ctx}) or max(/t/uint64,#1:now-{$M: ctx})'
+ ],
+ [
+ '{t:uint64.min(#1)} or {t:uint64.min( #1)} or {t:uint64.min("#1")} or {t:uint64.min( "#1")} or {t:uint64.min( "#1" )} or {t:uint64.min(5)} or {t:uint64.min( 5)} or {t:uint64.min("5")} or {t:uint64.min( "5")} or {t:uint64.min( "5" )} or {t:uint64.min(10m)} or {t:uint64.min( 10m)} or {t:uint64.min("10m")} or {t:uint64.min( "10m")} or {t:uint64.min( "10m" )} or {t:uint64.min({$M: ctx})} or {t:uint64.min( {$M: ctx})} or {t:uint64.min("{$M: ctx}")} or {t:uint64.min( "{$M: ctx}")} or {t:uint64.min( "{$M: ctx}" )} or {t:uint64.min(#1,)} or {t:uint64.min(#1,3600)} or {t:uint64.min(#1, 3600)} or {t:uint64.min(#1,"3600")} or {t:uint64.min(#1, "3600")} or {t:uint64.min(#1, "3600" )} or {t:uint64.min(#1,1h)} or {t:uint64.min(#1, 1h)} or {t:uint64.min(#1,"1h")} or {t:uint64.min(#1, "1h")} or {t:uint64.min(#1, "1h" )} or {t:uint64.min(#1,{$M: ctx})} or {t:uint64.min(#1, {$M: ctx})} or {t:uint64.min(#1,"{$M: ctx}")} or {t:uint64.min(#1, "{$M: ctx}")} or {t:uint64.min(#1, "{$M: ctx}" )}',
+ 'min(/t/uint64,#1) or min(/t/uint64,#1) or min(/t/uint64,#1) or min(/t/uint64,#1) or min(/t/uint64,#1) or min(/t/uint64,5s) or min(/t/uint64,5s) or min(/t/uint64,5s) or min(/t/uint64,5s) or min(/t/uint64,5s) or min(/t/uint64,10m) or min(/t/uint64,10m) or min(/t/uint64,10m) or min(/t/uint64,10m) or min(/t/uint64,10m) or min(/t/uint64,{$M: ctx}) or min(/t/uint64,{$M: ctx}) or min(/t/uint64,{$M: ctx}) or min(/t/uint64,{$M: ctx}) or min(/t/uint64,{$M: ctx}) or min(/t/uint64,#1) or min(/t/uint64,#1:now-3600s) or min(/t/uint64,#1:now-3600s) or min(/t/uint64,#1:now-3600s) or min(/t/uint64,#1:now-3600s) or min(/t/uint64,#1:now-3600s) or min(/t/uint64,#1:now-1h) or min(/t/uint64,#1:now-1h) or min(/t/uint64,#1:now-1h) or min(/t/uint64,#1:now-1h) or min(/t/uint64,#1:now-1h) or min(/t/uint64,#1:now-{$M: ctx}) or min(/t/uint64,#1:now-{$M: ctx}) or min(/t/uint64,#1:now-{$M: ctx}) or min(/t/uint64,#1:now-{$M: ctx}) or min(/t/uint64,#1:now-{$M: ctx})'
+ ],
+ [
+ '{t:uint64.nodata(3600)} or {t:uint64.nodata( 1h)} or {t:uint64.nodata("24h",strict)} or {t:uint64.nodata( "1d", strict)} or {t:uint64.nodata( "1w" , "strict" )}',
+ 'nodata(/t/uint64,3600s) or nodata(/t/uint64,1h) or nodata(/t/uint64,24h,"strict") or nodata(/t/uint64,1d,"strict") or nodata(/t/uint64,1w,"strict")'
+ ],
+ [
+ '{t:uint64.now()} or {t:uint64.now( )} or {t:uint64.now( "" )} or {t:uint64.now( " " )} or {t:uint64.now( "text" )}',
+ '(now() or now() or now() or now() or now()) or (last(/t/uint64)<>last(/t/uint64))'
+ ],
+ [
+ '{t:uint64.percentile(#1,,10)} or {t:uint64.percentile( #1, , 20)} or {t:uint64.percentile("#1","","30")} or {t:uint64.percentile( "#1", "", "40")} or {t:uint64.percentile( "#1" , "" , "50" )} or {t:uint64.percentile(5,,5.1234)} or {t:uint64.percentile( 5, , 6.2345)} or {t:uint64.percentile("5","","7.3456")} or {t:uint64.percentile( "5", "", "8.4567")} or {t:uint64.percentile( "5" , "" , "9.5678" )} or {t:uint64.percentile(10m,,{$M: ctx})} or {t:uint64.percentile( 10m, , {$M: ctx})} or {t:uint64.percentile("10m","","{$M: ctx}")} or {t:uint64.percentile( "10m", "", "{$M: ctx}")} or {t:uint64.percentile( "10m" , "" , "{$M: ctx}" )} or {t:uint64.percentile({$M: ctx},,1)} or {t:uint64.percentile( {$M: ctx},,1)} or {t:uint64.percentile("{$M: ctx}",,1)} or {t:uint64.percentile( "{$M: ctx}",,1)} or {t:uint64.percentile( "{$M: ctx}" ,,1)} or {t:uint64.percentile(#1,,30)} or {t:uint64.percentile(#1,3600,1)} or {t:uint64.percentile(#1, 3600,1)} or {t:uint64.percentile(#1,"3600",1)} or {t:uint64.percentile(#1, "3600",1)} or {t:uint64.percentile(#1, "3600" ,1)} or {t:uint64.percentile(#1,1h,1)} or {t:uint64.percentile(#1, 1h,1)} or {t:uint64.percentile(#1,"1h",1)} or {t:uint64.percentile(#1, "1h",1)} or {t:uint64.percentile(#1, "1h" ,1)} or {t:uint64.percentile(#1,{$M: ctx},1)} or {t:uint64.percentile(#1, {$M: ctx},1)} or {t:uint64.percentile(#1,"{$M: ctx}",1)} or {t:uint64.percentile(#1, "{$M: ctx}",1)} or {t:uint64.percentile(#1, "{$M: ctx}" ,1)}',
+ 'percentile(/t/uint64,#1,10) or percentile(/t/uint64,#1,20) or percentile(/t/uint64,#1,30) or percentile(/t/uint64,#1,40) or percentile(/t/uint64,#1,50) or percentile(/t/uint64,5s,5.1234) or percentile(/t/uint64,5s,6.2345) or percentile(/t/uint64,5s,7.3456) or percentile(/t/uint64,5s,8.4567) or percentile(/t/uint64,5s,9.5678) or percentile(/t/uint64,10m,{$M: ctx}) or percentile(/t/uint64,10m,{$M: ctx}) or percentile(/t/uint64,10m,{$M: ctx}) or percentile(/t/uint64,10m,{$M: ctx}) or percentile(/t/uint64,10m,{$M: ctx}) or percentile(/t/uint64,{$M: ctx},1) or percentile(/t/uint64,{$M: ctx},1) or percentile(/t/uint64,{$M: ctx},1) or percentile(/t/uint64,{$M: ctx},1) or percentile(/t/uint64,{$M: ctx},1) or percentile(/t/uint64,#1,30) or percentile(/t/uint64,#1:now-3600s,1) or percentile(/t/uint64,#1:now-3600s,1) or percentile(/t/uint64,#1:now-3600s,1) or percentile(/t/uint64,#1:now-3600s,1) or percentile(/t/uint64,#1:now-3600s,1) or percentile(/t/uint64,#1:now-1h,1) or percentile(/t/uint64,#1:now-1h,1) or percentile(/t/uint64,#1:now-1h,1) or percentile(/t/uint64,#1:now-1h,1) or percentile(/t/uint64,#1:now-1h,1) or percentile(/t/uint64,#1:now-{$M: ctx},1) or percentile(/t/uint64,#1:now-{$M: ctx},1) or percentile(/t/uint64,#1:now-{$M: ctx},1) or percentile(/t/uint64,#1:now-{$M: ctx},1) or percentile(/t/uint64,#1:now-{$M: ctx},1)'
+ ],
+ [
+ '{t:uint64.prev()} or {t:uint64.prev( )} or {t:uint64.prev( "" )} or {t:uint64.prev( " " )} or {t:uint64.prev( "text" )}',
+ 'last(/t/uint64,#2) or last(/t/uint64,#2) or last(/t/uint64,#2) or last(/t/uint64,#2) or last(/t/uint64,#2)'
+ ],
+ [
+ '{t:uint64.sum(#1)} or {t:uint64.sum( #1)} or {t:uint64.sum("#1")} or {t:uint64.sum( "#1")} or {t:uint64.sum( "#1" )} or {t:uint64.sum(5)} or {t:uint64.sum( 5)} or {t:uint64.sum("5")} or {t:uint64.sum( "5")} or {t:uint64.sum( "5" )} or {t:uint64.sum(10m)} or {t:uint64.sum( 10m)} or {t:uint64.sum("10m")} or {t:uint64.sum( "10m")} or {t:uint64.sum( "10m" )} or {t:uint64.sum({$M: ctx})} or {t:uint64.sum( {$M: ctx})} or {t:uint64.sum("{$M: ctx}")} or {t:uint64.sum( "{$M: ctx}")} or {t:uint64.sum( "{$M: ctx}" )} or {t:uint64.sum(#1,)} or {t:uint64.sum(#1,3600)} or {t:uint64.sum(#1, 3600)} or {t:uint64.sum(#1,"3600")} or {t:uint64.sum(#1, "3600")} or {t:uint64.sum(#1, "3600" )} or {t:uint64.sum(#1,1h)} or {t:uint64.sum(#1, 1h)} or {t:uint64.sum(#1,"1h")} or {t:uint64.sum(#1, "1h")} or {t:uint64.sum(#1, "1h" )} or {t:uint64.sum(#1,{$M: ctx})} or {t:uint64.sum(#1, {$M: ctx})} or {t:uint64.sum(#1,"{$M: ctx}")} or {t:uint64.sum(#1, "{$M: ctx}")} or {t:uint64.sum(#1, "{$M: ctx}" )}',
+ 'sum(/t/uint64,#1) or sum(/t/uint64,#1) or sum(/t/uint64,#1) or sum(/t/uint64,#1) or sum(/t/uint64,#1) or sum(/t/uint64,5s) or sum(/t/uint64,5s) or sum(/t/uint64,5s) or sum(/t/uint64,5s) or sum(/t/uint64,5s) or sum(/t/uint64,10m) or sum(/t/uint64,10m) or sum(/t/uint64,10m) or sum(/t/uint64,10m) or sum(/t/uint64,10m) or sum(/t/uint64,{$M: ctx}) or sum(/t/uint64,{$M: ctx}) or sum(/t/uint64,{$M: ctx}) or sum(/t/uint64,{$M: ctx}) or sum(/t/uint64,{$M: ctx}) or sum(/t/uint64,#1) or sum(/t/uint64,#1:now-3600s) or sum(/t/uint64,#1:now-3600s) or sum(/t/uint64,#1:now-3600s) or sum(/t/uint64,#1:now-3600s) or sum(/t/uint64,#1:now-3600s) or sum(/t/uint64,#1:now-1h) or sum(/t/uint64,#1:now-1h) or sum(/t/uint64,#1:now-1h) or sum(/t/uint64,#1:now-1h) or sum(/t/uint64,#1:now-1h) or sum(/t/uint64,#1:now-{$M: ctx}) or sum(/t/uint64,#1:now-{$M: ctx}) or sum(/t/uint64,#1:now-{$M: ctx}) or sum(/t/uint64,#1:now-{$M: ctx}) or sum(/t/uint64,#1:now-{$M: ctx})'
+ ],
+ [
+ '{t:uint64.time()} or {t:uint64.time( )} or {t:uint64.time( "" )} or {t:uint64.time( " " )} or {t:uint64.time( "text" )}',
+ '(time() or time() or time() or time() or time()) or (last(/t/uint64)<>last(/t/uint64))'
+ ],
+ [
+ '{t:uint64.timeleft(#1,,1)} or {t:uint64.timeleft( #1, , 1)} or {t:uint64.timeleft("#1","","1")} or {t:uint64.timeleft( "#1", "", "1")} or {t:uint64.timeleft( "#1" , "" , "1" )} or {t:uint64.timeleft(5,,86400)} or {t:uint64.timeleft( 5, , 86400)} or {t:uint64.timeleft("5","","86400")} or {t:uint64.timeleft( "5", "", "86400")} or {t:uint64.timeleft( "5" , "" , "86400" )} or {t:uint64.timeleft(10m,,{$M: ctx})} or {t:uint64.timeleft( 10m, , {$M: ctx})} or {t:uint64.timeleft("10m","","{$M: ctx}")} or {t:uint64.timeleft( "10m", "", "{$M: ctx}")} or {t:uint64.timeleft( "10m" , "" , "{$M: ctx}" )} or {t:uint64.timeleft({$M: ctx},,1.2345,polynomial1)} or {t:uint64.timeleft( {$M: ctx},,1.7653, polynomial2)} or {t:uint64.timeleft("{$M: ctx}",,1.3456,"polynomial3")} or {t:uint64.timeleft( "{$M: ctx}",,1.45, "polynomial4")} or {t:uint64.timeleft( "{$M: ctx}" ,,1.45, "polynomial5" )} or {t:uint64.timeleft(#1,,30,)} or {t:uint64.timeleft(#1,3600,1,polynomial6)} or {t:uint64.timeleft(#1, 3600,1, linear)} or {t:uint64.timeleft(#1,"3600",1,"exponential")} or {t:uint64.timeleft(#1, "3600",1, "logarithmic")} or {t:uint64.timeleft(#1, "3600" ,1, "power" )} or {t:uint64.timeleft(#1,1h,1)} or {t:uint64.timeleft(#1, 1h,1)} or {t:uint64.timeleft(#1,"1h",1)} or {t:uint64.timeleft(#1, "1h",1)} or {t:uint64.timeleft(#1, "1h" ,1)} or {t:uint64.timeleft(#1,{$M: ctx},1)} or {t:uint64.timeleft(#1, {$M: ctx},1)} or {t:uint64.timeleft(#1,"{$M: ctx}",1)} or {t:uint64.timeleft(#1, "{$M: ctx}",1)} or {t:uint64.timeleft(#1, "{$M: ctx}" ,1d)}',
+ 'timeleft(/t/uint64,#1,1) or timeleft(/t/uint64,#1,1) or timeleft(/t/uint64,#1,1) or timeleft(/t/uint64,#1,1) or timeleft(/t/uint64,#1,1) or timeleft(/t/uint64,5s,86400) or timeleft(/t/uint64,5s,86400) or timeleft(/t/uint64,5s,86400) or timeleft(/t/uint64,5s,86400) or timeleft(/t/uint64,5s,86400) or timeleft(/t/uint64,10m,{$M: ctx}) or timeleft(/t/uint64,10m,{$M: ctx}) or timeleft(/t/uint64,10m,{$M: ctx}) or timeleft(/t/uint64,10m,{$M: ctx}) or timeleft(/t/uint64,10m,{$M: ctx}) or timeleft(/t/uint64,{$M: ctx},1.2345,"polynomial1") or timeleft(/t/uint64,{$M: ctx},1.7653,"polynomial2") or timeleft(/t/uint64,{$M: ctx},1.3456,"polynomial3") or timeleft(/t/uint64,{$M: ctx},1.45,"polynomial4") or timeleft(/t/uint64,{$M: ctx},1.45,"polynomial5") or timeleft(/t/uint64,#1,30) or timeleft(/t/uint64,#1:now-3600s,1,"polynomial6") or timeleft(/t/uint64,#1:now-3600s,1,"linear") or timeleft(/t/uint64,#1:now-3600s,1,"exponential") or timeleft(/t/uint64,#1:now-3600s,1,"logarithmic") or timeleft(/t/uint64,#1:now-3600s,1,"power") or timeleft(/t/uint64,#1:now-1h,1) or timeleft(/t/uint64,#1:now-1h,1) or timeleft(/t/uint64,#1:now-1h,1) or timeleft(/t/uint64,#1:now-1h,1) or timeleft(/t/uint64,#1:now-1h,1) or timeleft(/t/uint64,#1:now-{$M: ctx},1) or timeleft(/t/uint64,#1:now-{$M: ctx},1) or timeleft(/t/uint64,#1:now-{$M: ctx},1) or timeleft(/t/uint64,#1:now-{$M: ctx},1) or timeleft(/t/uint64,#1:now-{$M: ctx},1d)'
+ ],
+ [
+ '{t:uint64.trendavg(1h,now/h)} or {t:uint64.trendavg( 1d, now/d)} or {t:uint64.trendavg("1w","now/w")} or {t:uint64.trendavg( "1M", "now/M")} or {t:uint64.trendavg( "1y" , "now/y")} or {t:uint64.trendavg({$M: ctx},{$M: ctx})} or {t:uint64.trendavg( {$M: ctx}, {$M: ctx})} or {t:uint64.trendavg("{$M: ctx}","{$M: ctx}")} or {t:uint64.trendavg( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trendavg( "{$M: ctx}" , "{$M: ctx}" )}',
+ 'trendavg(/t/uint64,1h:now/h) or trendavg(/t/uint64,1d:now/d) or trendavg(/t/uint64,1w:now/w) or trendavg(/t/uint64,1M:now/M) or trendavg(/t/uint64,1y:now/y) or trendavg(/t/uint64,{$M: ctx}:{$M: ctx}) or trendavg(/t/uint64,{$M: ctx}:{$M: ctx}) or trendavg(/t/uint64,{$M: ctx}:{$M: ctx}) or trendavg(/t/uint64,{$M: ctx}:{$M: ctx}) or trendavg(/t/uint64,{$M: ctx}:{$M: ctx})'
+ ],
+ [
+ '{t:uint64.trendcount(1h,now/h)} or {t:uint64.trendcount( 1d, now/d)} or {t:uint64.trendcount("1w","now/w")} or {t:uint64.trendcount( "1M", "now/M")} or {t:uint64.trendcount( "1y" , "now/y")} or {t:uint64.trendcount({$M: ctx},{$M: ctx})} or {t:uint64.trendcount( {$M: ctx}, {$M: ctx})} or {t:uint64.trendcount("{$M: ctx}","{$M: ctx}")} or {t:uint64.trendcount( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trendcount( "{$M: ctx}" , "{$M: ctx}" )}',
+ 'trendcount(/t/uint64,1h:now/h) or trendcount(/t/uint64,1d:now/d) or trendcount(/t/uint64,1w:now/w) or trendcount(/t/uint64,1M:now/M) or trendcount(/t/uint64,1y:now/y) or trendcount(/t/uint64,{$M: ctx}:{$M: ctx}) or trendcount(/t/uint64,{$M: ctx}:{$M: ctx}) or trendcount(/t/uint64,{$M: ctx}:{$M: ctx}) or trendcount(/t/uint64,{$M: ctx}:{$M: ctx}) or trendcount(/t/uint64,{$M: ctx}:{$M: ctx})'
+ ],
+ [
+ '{t:uint64.trenddelta(1h,now/h)} or {t:uint64.trenddelta( 1d, now/d)} or {t:uint64.trenddelta("1w","now/w")} or {t:uint64.trenddelta( "1M", "now/M")} or {t:uint64.trenddelta( "1y" , "now/y")} or {t:uint64.trenddelta({$M: ctx},{$M: ctx})} or {t:uint64.trenddelta( {$M: ctx}, {$M: ctx})} or {t:uint64.trenddelta("{$M: ctx}","{$M: ctx}")} or {t:uint64.trenddelta( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trenddelta( "{$M: ctx}" , "{$M: ctx}" )}',
+ '(trendmax(/t/uint64,1h:now/h)-trendmin(/t/uint64,1h:now/h)) or (trendmax(/t/uint64,1d:now/d)-trendmin(/t/uint64,1d:now/d)) or (trendmax(/t/uint64,1w:now/w)-trendmin(/t/uint64,1w:now/w)) or (trendmax(/t/uint64,1M:now/M)-trendmin(/t/uint64,1M:now/M)) or (trendmax(/t/uint64,1y:now/y)-trendmin(/t/uint64,1y:now/y)) or (trendmax(/t/uint64,{$M: ctx}:{$M: ctx})-trendmin(/t/uint64,{$M: ctx}:{$M: ctx})) or (trendmax(/t/uint64,{$M: ctx}:{$M: ctx})-trendmin(/t/uint64,{$M: ctx}:{$M: ctx})) or (trendmax(/t/uint64,{$M: ctx}:{$M: ctx})-trendmin(/t/uint64,{$M: ctx}:{$M: ctx})) or (trendmax(/t/uint64,{$M: ctx}:{$M: ctx})-trendmin(/t/uint64,{$M: ctx}:{$M: ctx})) or (trendmax(/t/uint64,{$M: ctx}:{$M: ctx})-trendmin(/t/uint64,{$M: ctx}:{$M: ctx}))'
+ ],
+ [
+ '{t:uint64.trendmax(1h,now/h)} or {t:uint64.trendmax( 1d, now/d)} or {t:uint64.trendmax("1w","now/w")} or {t:uint64.trendmax( "1M", "now/M")} or {t:uint64.trendmax( "1y" , "now/y")} or {t:uint64.trendmax({$M: ctx},{$M: ctx})} or {t:uint64.trendmax( {$M: ctx}, {$M: ctx})} or {t:uint64.trendmax("{$M: ctx}","{$M: ctx}")} or {t:uint64.trendmax( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trendmax( "{$M: ctx}" , "{$M: ctx}" )}',
+ 'trendmax(/t/uint64,1h:now/h) or trendmax(/t/uint64,1d:now/d) or trendmax(/t/uint64,1w:now/w) or trendmax(/t/uint64,1M:now/M) or trendmax(/t/uint64,1y:now/y) or trendmax(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmax(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmax(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmax(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmax(/t/uint64,{$M: ctx}:{$M: ctx})'
+ ],
+ [
+ '{t:uint64.trendmin(1h,now/h)} or {t:uint64.trendmin( 1d, now/d)} or {t:uint64.trendmin("1w","now/w")} or {t:uint64.trendmin( "1M", "now/M")} or {t:uint64.trendmin( "1y" , "now/y")} or {t:uint64.trendmin({$M: ctx},{$M: ctx})} or {t:uint64.trendmin( {$M: ctx}, {$M: ctx})} or {t:uint64.trendmin("{$M: ctx}","{$M: ctx}")} or {t:uint64.trendmin( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trendmin( "{$M: ctx}" , "{$M: ctx}" )}',
+ 'trendmin(/t/uint64,1h:now/h) or trendmin(/t/uint64,1d:now/d) or trendmin(/t/uint64,1w:now/w) or trendmin(/t/uint64,1M:now/M) or trendmin(/t/uint64,1y:now/y) or trendmin(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmin(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmin(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmin(/t/uint64,{$M: ctx}:{$M: ctx}) or trendmin(/t/uint64,{$M: ctx}:{$M: ctx})'
+ ],
+ [
+ '{t:uint64.trendsum(1h,now/h)} or {t:uint64.trendsum( 1d, now/d)} or {t:uint64.trendsum("1w","now/w")} or {t:uint64.trendsum( "1M", "now/M")} or {t:uint64.trendsum( "1y" , "now/y")} or {t:uint64.trendsum({$M: ctx},{$M: ctx})} or {t:uint64.trendsum( {$M: ctx}, {$M: ctx})} or {t:uint64.trendsum("{$M: ctx}","{$M: ctx}")} or {t:uint64.trendsum( "{$M: ctx}", "{$M: ctx}")} or {t:uint64.trendsum( "{$M: ctx}" , "{$M: ctx}" )}',
+ 'trendsum(/t/uint64,1h:now/h) or trendsum(/t/uint64,1d:now/d) or trendsum(/t/uint64,1w:now/w) or trendsum(/t/uint64,1M:now/M) or trendsum(/t/uint64,1y:now/y) or trendsum(/t/uint64,{$M: ctx}:{$M: ctx}) or trendsum(/t/uint64,{$M: ctx}:{$M: ctx}) or trendsum(/t/uint64,{$M: ctx}:{$M: ctx}) or trendsum(/t/uint64,{$M: ctx}:{$M: ctx}) or trendsum(/t/uint64,{$M: ctx}:{$M: ctx})'
+ ],
+ [
+ '{Trapper:trap[1].abschange()} > 10'.
+ ' and {Trapper:trap[1].abschange()} <> "{20727}"',
+
+ 'abs(change(/Trapper/trap[1])) > 10'.
+ ' and abs(change(/Trapper/trap[1])) <> "{20727}"'
+ ],
+ [
+ '{Trapper:trap[1].avg(30m)} > 0'.
+ ' and {Trapper:trap[1].avg(60)} > 1'.
+ ' and {Trapper:trap[1].avg(#10)} > 3'.
+ ' and {Trapper:trap[1].avg(60,3600)} > 4'.
+ ' and {Trapper:trap[1].avg(1m,1h)} > 5',
+
+ 'avg(/Trapper/trap[1],30m) > 0'.
+ ' and avg(/Trapper/trap[1],60s) > 1'.
+ ' and avg(/Trapper/trap[1],#10) > 3'.
+ ' and avg(/Trapper/trap[1],60s:now-3600s) > 4'.
+ ' and avg(/Trapper/trap[1],1m:now-1h) > 5'
+ ],
+ [
+ '{Trapper:trap[1].change()} = 10',
+ 'change(/Trapper/trap[1]) = 10'
+ ],
+ [
+ '{Trapper:trap[1].date()} > 0'.
+ ' and {Trapper:trap[2].last()} > 0',
+
+ 'date() > 0'.
+ ' and last(/Trapper/trap[2]) > 0'
+ ],
+ [
+ '{Trapper:trap[1].dayofmonth()} > 0 and {Trapper2:trap[1].last()} > 0',
+ '(dayofmonth() > 0 and last(/Trapper2/trap[1]) > 0) or (last(/Trapper/trap[1])<>last(/Trapper/trap[1]))'
+ ],
+ [
+ '{Trapper:trap[1].delta(30m)} > 0'.
+ ' and {Trapper:trap[1].delta(60)} > 1'.
+ ' and {Trapper:trap[1].delta(#10)} > 3'.
+ ' and {Trapper:trap[1].delta(60,3600)} > 4'.
+ ' and {Trapper:trap[1].delta(1m,1h)} > 5',
+
+ '(max(/Trapper/trap[1],30m)-min(/Trapper/trap[1],30m)) > 0'.
+ ' and (max(/Trapper/trap[1],60s)-min(/Trapper/trap[1],60s)) > 1'.
+ ' and (max(/Trapper/trap[1],#10)-min(/Trapper/trap[1],#10)) > 3'.
+ ' and (max(/Trapper/trap[1],60s:now-3600s)-min(/Trapper/trap[1],60s:now-3600s)) > 4'.
+ ' and (max(/Trapper/trap[1],1m:now-1h)-min(/Trapper/trap[1],1m:now-1h)) > 5'
+ ],
+ [
+ '{Trapper:trap[1].diff()} = 0',
+ '(last(/Trapper/trap[1],#1)<>last(/Trapper/trap[1],#2)) = 0'
+ ],
+ [
+ '{Trapper:trap[1].fuzzytime(60)} > 0',
+ 'fuzzytime(/Trapper/trap[1],60s) > 0'
+ ],
+ [
+ '{Trapper:trap[1].max(30m)} > 0'.
+ ' and {Trapper:trap[1].max(60)} > 1'.
+ ' and {Trapper:trap[1].max(#10)} > 3'.
+ ' and {Trapper:trap[1].max(60,3600)} > 4'.
+ ' and {Trapper:trap[1].max(1m,1h)} > 5',
+
+ 'max(/Trapper/trap[1],30m) > 0'.
+ ' and max(/Trapper/trap[1],60s) > 1'.
+ ' and max(/Trapper/trap[1],#10) > 3'.
+ ' and max(/Trapper/trap[1],60s:now-3600s) > 4'.
+ ' and max(/Trapper/trap[1],1m:now-1h) > 5'
+ ],
+ [
+ '{Trapper:trap[1].min(30m)} > 0'.
+ ' and {Trapper:trap[1].min(60)} > 1'.
+ ' and {Trapper:trap[1].min(#10)} > 3'.
+ ' and {Trapper:trap[1].min(60,3600)} > 4'.
+ ' and {Trapper:trap[1].min(1m,1h)} > 5',
+
+ 'min(/Trapper/trap[1],30m) > 0'.
+ ' and min(/Trapper/trap[1],60s) > 1'.
+ ' and min(/Trapper/trap[1],#10) > 3'.
+ ' and min(/Trapper/trap[1],60s:now-3600s) > 4'.
+ ' and min(/Trapper/trap[1],1m:now-1h) > 5'
+ ],
+ [
+ '{Trapper:trap[1].nodata(60)} > 0 and {Trapper:trap[1].nodata(5m)} > 0',
+ 'nodata(/Trapper/trap[1],60s) > 0 and nodata(/Trapper/trap[1],5m) > 0'
+ ],
+ [
+ '{Trapper:trap[1].now()} > 0 and {Trapper2:trap[1].now()} > 0',
+
+ '(now() > 0 and now() > 0)'.
+ ' or (last(/Trapper/trap[1])<>last(/Trapper/trap[1])) or (last(/Trapper2/trap[1])<>last(/Trapper2/trap[1]))'
+ ],
+ [
+ '{Trapper:trap[1].percentile(30m,,50)} > 0'.
+ ' and {Trapper:trap[1].percentile(60, ,60)} > 1'.
+ ' and {Trapper:trap[1].percentile(#10, ,70)} > 3'.
+ ' and {Trapper:trap[1].percentile(60,3600,80)} > 4'.
+ ' and {Trapper:trap[1].percentile(1m,1h,90)} > 5',
+
+ 'percentile(/Trapper/trap[1],30m,50) > 0'.
+ ' and percentile(/Trapper/trap[1],60s,60) > 1'.
+ ' and percentile(/Trapper/trap[1],#10,70) > 3'.
+ ' and percentile(/Trapper/trap[1],60s:now-3600s,80) > 4'.
+ ' and percentile(/Trapper/trap[1],1m:now-1h,90) > 5'
+ ],
+ [
+ '{Trapper:trap[1].sum(30m)} > 0'.
+ ' and {Trapper:trap[1].sum(60)} > 1'.
+ ' and {Trapper:trap[1].sum(#10)} > 3'.
+ ' and {Trapper:trap[1].sum(60,3600)} > 4'.
+ ' and {Trapper:trap[1].sum(1m,1h)} > 5',
+
+ 'sum(/Trapper/trap[1],30m) > 0'.
+ ' and sum(/Trapper/trap[1],60s) > 1'.
+ ' and sum(/Trapper/trap[1],#10) > 3'.
+ ' and sum(/Trapper/trap[1],60s:now-3600s) > 4'.
+ ' and sum(/Trapper/trap[1],1m:now-1h) > 5'
+ ],
+ [
+ '{Trapper:trap[1].time()} > 0 and {Trapper:trap[1].last()} <> 0',
+ 'time() > 0 and last(/Trapper/trap[1]) <> 0'
+ ],
+ [
+ '{Trapper:trap[1].trendavg(1h, now/h-1d)} > 0',
+ 'trendavg(/Trapper/trap[1],1h:now/h-1d) > 0'
+ ],
+ [
+ '{Trapper:trap[1].trendcount(1h, now/h-1d)} > 0',
+ 'trendcount(/Trapper/trap[1],1h:now/h-1d) > 0'
+ ],
+ [
+ '1 and {Trapper:trap[1].trenddelta(1h, now/h-1d)} > 0',
+ '1 and (trendmax(/Trapper/trap[1],1h:now/h-1d)-trendmin(/Trapper/trap[1],1h:now/h-1d)) > 0'
+ ],
+ [
+ '{Trapper:trap[1].trendmax(1h, now/h-1d)} > 0',
+ 'trendmax(/Trapper/trap[1],1h:now/h-1d) > 0'
+ ],
+ [
+ '{Trapper:trap[1].trendmin(1h, now/h-1d)} > 0',
+ 'trendmin(/Trapper/trap[1],1h:now/h-1d) > 0'
+ ],
+ [
+ '{Trapper:trap[1].trendsum(1h, now/h-1d)} > 0',
+ 'trendsum(/Trapper/trap[1],1h:now/h-1d) > 0'
+ ],
+ [
+ '{Trapper:trap[2].band(#1, 32)} > 0 and {Trapper:trap[2].band(#2, 64, 1h)} > 0',
+ 'bitand(last(/Trapper/trap[2],#1),32) > 0 and bitand(last(/Trapper/trap[2],#2:now-1h),64) > 0'
+ ],
+ [
+ '{Trapper:trap[2].forecast(#10,,100)} > 0'.
+ ' and {Trapper:trap[2].forecast(3600,7200,600,linear,avg)} > 0'.
+ ' and {Trapper:trap[2].forecast(30m,1d,600,,avg)} > 0',
+
+ 'forecast(/Trapper/trap[2],#10,100s) > 0'.
+ ' and forecast(/Trapper/trap[2],3600s:now-7200s,600s,"linear","avg") > 0'.
+ ' and forecast(/Trapper/trap[2],30m:now-1d,600s,,"avg") > 0'
+ ],
+
+ [
+ '{Trapper:trap[2].timeleft(#10,,100)} > 0'.
+ ' and {Trapper:trap[2].timeleft(3600,7200,600,linear)} > 0'.
+ ' and {Trapper:trap[2].timeleft(30m,1d,600)} > 0',
+
+ 'timeleft(/Trapper/trap[2],#10,100) > 0'.
+ ' and timeleft(/Trapper/trap[2],3600s:now-7200s,600,"linear") > 0'.
+ ' and timeleft(/Trapper/trap[2],30m:now-1d,600) > 0'
+ ],
+ [
+ '{Trapper:trap[3].count(#1, 0, eq)} > 0'.
+ ' and {Trapper:trap[3].count(#1,0,eq)} > 0'.
+ ' and {Trapper:trap[3].count(5m, "xyz", regexp, 2h)} > 0'.
+ ' and {Trapper:trap[3].count(5m,"xyz",regexp,2h)} > 0'.
+ ' and {Trapper:trap[2].count(5m, 10, iregexp, 1h)} > 0'.
+ ' and {Trapper:trap[1].count(5m, 100, gt, 2d)} > 0'.
+ ' and {Trapper:trap[1].count(1m, 32, band)} > 0'.
+ ' and {Trapper:trap[1].count(1m, 32/8, band)} > 0'.
+ ' and {Trapper:trap[1].count(10m)} > 0',
+
+ 'count(/Trapper/trap[3],#1,"eq","0") > 0'.
+ ' and count(/Trapper/trap[3],#1,"eq","0") > 0'.
+ ' and count(/Trapper/trap[3],5m:now-2h,"regexp","xyz") > 0'.
+ ' and count(/Trapper/trap[3],5m:now-2h,"regexp","xyz") > 0'.
+ ' and count(/Trapper/trap[2],5m:now-1h,"iregexp","10") > 0'.
+ ' and count(/Trapper/trap[1],5m:now-2d,"gt","100") > 0'.
+ ' and count(/Trapper/trap[1],1m,"bitand","32") > 0'.
+ ' and count(/Trapper/trap[1],1m,"bitand","32/8") > 0'.
+ ' and count(/Trapper/trap[1],10m) > 0'
+ ],
+ [
+ '{Trapper:trap[3].iregexp("^error", #10)} > 0'.
+ ' and {Trapper:trap[3].iregexp("^critical", 60)} > 0'.
+ ' and {Trapper:trap[3].iregexp("^warning", 5m)} > 0',
+
+ 'find(/Trapper/trap[3],#10,"iregexp","^error") > 0'.
+ ' and find(/Trapper/trap[3],60s,"iregexp","^critical") > 0'.
+ ' and find(/Trapper/trap[3],5m,"iregexp","^warning") > 0'
+ ],
+ [
+ '{Trapper:trap[3].last()} > 0'.
+ ' and {Trapper:trap[3].last(#5)} > 0'.
+ ' and {Trapper:trap[3].last(#10,3600)} > 0'.
+ ' and {Trapper:trap[3].last(#1,1d)} > 0',
+
+ 'last(/Trapper/trap[3]) > 0'.
+ ' and last(/Trapper/trap[3],#5) > 0'.
+ ' and last(/Trapper/trap[3],#10:now-3600s) > 0'.
+ ' and last(/Trapper/trap[3],#1:now-1d) > 0'
+ ],
+ [
+ '{Trapper:trap[3].prev()} > 0',
+ 'last(/Trapper/trap[3],#2) > 0'
+ ],
+ [
+ '{Trapper:trap[3].regexp("^error", #10)} > 0'.
+ ' and {Trapper:trap[3].regexp("^critical", 60)} > 0'.
+ ' and {Trapper:trap[3].regexp("^warning", 5m)} > 0',
+
+ 'find(/Trapper/trap[3],#10,"regexp","^error") > 0'.
+ ' and find(/Trapper/trap[3],60s,"regexp","^critical") > 0'.
+ ' and find(/Trapper/trap[3],5m,"regexp","^warning") > 0'
+ ],
+ [
+ '{Trapper:trap[3].str("^error", #10)} > 0'.
+ ' and {Trapper:trap[3].str("^critical", 60)} > 0'.
+ ' and {Trapper:trap[3].str("^warning", 5m)} > 0',
+
+ 'find(/Trapper/trap[3],#10,"like","^error") > 0'.
+ ' and find(/Trapper/trap[3],60s,"like","^critical") > 0'.
+ ' and find(/Trapper/trap[3],5m,"like","^warning") > 0'
+ ],
+ [
+ '{Trapper:trap[3].strlen(30m)} > 0'.
+ ' and {Trapper:trap[3].strlen(60)} > 1'.
+ ' and {Trapper:trap[3].strlen(#10)} > 3'.
+ ' and {Trapper:trap[3].strlen(60,3600)} > 4'.
+ ' and {Trapper:trap[3].strlen(1m,1h)} > 5',
+
+ 'length(last(/Trapper/trap[3])) > 0'.
+ ' and length(last(/Trapper/trap[3])) > 1'.
+ ' and length(last(/Trapper/trap[3],#10)) > 3'.
+ ' and length(last(/Trapper/trap[3],#1:now-3600s)) > 4'.
+ ' and length(last(/Trapper/trap[3],#1:now-1h)) > 5'
+ ],
+ [
+ '{Trapper:trap[4].logeventid("^error")} > 0',
+ 'logeventid(/Trapper/trap[4],,"^error") > 0'
+ ],
+ [
+ '{Trapper:trap[4].logseverity()} > 0',
+ 'logseverity(/Trapper/trap[4]) > 0'
+ ],
+ [
+ '{Trapper:trap[4].logsource("^system$")} > 0',
+ 'logsource(/Trapper/trap[4],,"^system$") > 0'
+ ],
+ [
+ '{Trapper:trap[1].change()} = 10'.
+ ' or {Trapper:trap[2].change()} = 100'.
+ ' or {Trapper:trap[3].str(error)} <> 0',
+
+ 'change(/Trapper/trap[1]) = 10'.
+ ' or change(/Trapper/trap[2]) = 100'.
+ ' or find(/Trapper/trap[3],,"like","error") <> 0'
+ ],
+ [
+ '{Trapper:trap[1].dayofweek()} > 0'.
+ ' or {Trapper:trap[2].last()} > 0',
+
+ 'dayofweek() > 0'.
+ ' or last(/Trapper/trap[2]) > 0'
+ ],
+ [
+ '{Trapper:trap[1].dayofweek()} > 0',
+ '(dayofweek() > 0) or (last(/Trapper/trap[1])<>last(/Trapper/trap[1]))'
+ ],
+ [
+ '{Trapper:trap[1].dayofweek()} > 0'.
+ ' and {Host:trap[1].last()} > 0',
+
+ '(dayofweek() > 0 and last(/Host/trap[1]) > 0)'.
+ ' or (last(/Trapper/trap[1])<>last(/Trapper/trap[1]))'
+ ]
+ ];
+ }
+
+ public function shortExpressionProvideData() {
+ return [
+ 'enrich simple trigger expression' => [
+ [
+ 'expression' => '{dayofweek()}=0',
+ 'host' => 'Zabbix server',
+ 'item' => 'trap'
+ ],
+ 'expression' => '(dayofweek()=0) or (last(/Zabbix server/trap)<>last(/Zabbix server/trap))'
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider simpleProviderData
+ *
+ * @param string $old_expression
+ * @param string $new_expression
+ */
+ public function testSimpleConversion(string $old_expression, string $new_expression) {
+ $this->assertSame($new_expression, $this->converter->convert(['expression' => $old_expression]));
+ }
+
+ /**
+ * @dataProvider shortExpressionProvideData
+ *
+ * @param array $old_expression
+ * @param string $new_expression
+ */
+ public function testShortExpressionConversion(array $old_expression, string $new_expression) {
+ $this->assertSame($new_expression, $this->converter->convert($old_expression));
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CFunctionMacroParserTest.php b/ui/tests/unit/include/classes/parsers/C10FunctionMacroParserTest.php
index df6f356eeaf..b4802b58af5 100644
--- a/ui/tests/unit/include/classes/parsers/CFunctionMacroParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/C10FunctionMacroParserTest.php
@@ -21,10 +21,10 @@
use PHPUnit\Framework\TestCase;
-class CFunctionMacroParserTest extends CParserTest {
+class C10FunctionMacroParserTest extends CParserTest {
protected function getParser() {
- return new CFunctionMacroParser();
+ return new C10FunctionMacroParser();
}
public function dataProvider() {
@@ -145,7 +145,7 @@ class CFunctionMacroParserTest extends CParserTest {
static $parser = null;
if ($parser === null) {
- $parser = new CFunctionMacroParser(['18_simple_checks' => true]);
+ $parser = new C10FunctionMacroParser(['18_simple_checks' => true]);
}
$this->assertSame($expected, [
diff --git a/ui/tests/unit/include/classes/parsers/CFunctionParserTest.php b/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php
index 64339d41585..bc509eb7bda 100644
--- a/ui/tests/unit/include/classes/parsers/CFunctionParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -21,12 +21,12 @@
use PHPUnit\Framework\TestCase;
-class CFunctionParserTest extends TestCase {
+class C10FunctionParserTest extends TestCase {
/**
* An array of trigger functions and parsed results.
*/
- public static function dataProvider() {
+ public static function dataProviderParse() {
return [
// valid keys
[
@@ -37,12 +37,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '()',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 1
]
@@ -59,12 +59,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'func',
'parameters' => '',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '()',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 1
]
@@ -81,12 +81,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '""',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '("")',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 1
]
@@ -103,12 +103,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => ' ',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '( )',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 2
]
@@ -125,12 +125,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => ' ""',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '( "")',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 2
]
@@ -147,12 +147,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => ' "" ',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '( "" )',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 2
]
@@ -169,12 +169,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => 'a',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '(a)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'a',
'pos' => 1
]
@@ -191,12 +191,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => ' a',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '( a)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'a',
'pos' => 2
]
@@ -213,17 +213,17 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '"a",',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '("a",)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"a"',
'pos' => 1
],
1 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 5
]
@@ -240,22 +240,22 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => 'a,b,c',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '(a,b,c)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'a',
'pos' => 1
],
1 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'b',
'pos' => 3
],
2 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'c',
'pos' => 5
]
@@ -272,22 +272,22 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '"a","b","c"',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '("a","b","c")',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"a"',
'pos' => 1
],
1 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"b"',
'pos' => 5
],
2 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"c"',
'pos' => 9
]
@@ -304,82 +304,82 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '"\"aaa\"", "bbb","ccc" , "ddd" ,"", "","" , "" ,, , ,eee, fff,ggg , hhh" ',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '("\"aaa\"", "bbb","ccc" , "ddd" ,"", "","" , "" ,, , ,eee, fff,ggg , hhh" )',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"\"aaa\""',
'pos' => 1
],
1 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"bbb"',
'pos' => 12
],
2 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"ccc"',
'pos' => 18
],
3 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '"ddd"',
'pos' => 26
],
4 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 33
],
5 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 37
],
6 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 40
],
7 => [
- 'type' => CFunctionParser::PARAM_QUOTED,
+ 'type' => C10FunctionParser::PARAM_QUOTED,
'raw' => '""',
'pos' => 45
],
8 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 49
],
9 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 51
],
10 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 54
],
11 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'eee',
'pos' => 55
],
12 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'fff',
'pos' => 60
],
13 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'ggg ',
'pos' => 64
],
14 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'hhh" ',
'pos' => 70
]
@@ -396,17 +396,17 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => '("a",',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '(("a",)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '("a"',
'pos' => 1
],
1 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => '',
'pos' => 6
]
@@ -423,12 +423,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => 'ГУГЛ',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '(ГУГЛ)',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'ГУГЛ',
'pos' => 1
]
@@ -445,12 +445,12 @@ class CFunctionParserTest extends TestCase {
'function' => 'last',
'parameters' => 'ГУГЛ]',
'params_raw' => [
- 'type' => CFunctionParser::PARAM_ARRAY,
+ 'type' => C10FunctionParser::PARAM_ARRAY,
'raw' => '(ГУГЛ])',
'pos' => 4,
'parameters' => [
0 => [
- 'type' => CFunctionParser::PARAM_UNQUOTED,
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
'raw' => 'ГУГЛ]',
'pos' => 1
]
@@ -518,7 +518,7 @@ class CFunctionParserTest extends TestCase {
}
/**
- * @dataProvider dataProvider
+ * @dataProvider dataProviderParse
*
* @param string $source
* @param int $pos
@@ -528,7 +528,7 @@ class CFunctionParserTest extends TestCase {
static $function_parser = null;
if ($function_parser === null) {
- $function_parser = new CFunctionParser();
+ $function_parser = new C10FunctionParser();
}
$this->assertSame($expected, [
diff --git a/ui/tests/unit/include/classes/parsers/CTriggerExpressionTest.php b/ui/tests/unit/include/classes/parsers/C10TriggerExpressionTest.php
index acc55844c30..055de1e0e3f 100644
--- a/ui/tests/unit/include/classes/parsers/CTriggerExpressionTest.php
+++ b/ui/tests/unit/include/classes/parsers/C10TriggerExpressionTest.php
@@ -1,4 +1,4 @@
-<?php declare(strict_types=1);
+<?php
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -18,9 +18,10 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
use PHPUnit\Framework\TestCase;
-class CTriggerExpressionTest extends TestCase {
+class C10TriggerExpressionTest extends TestCase {
public static function provider() {
return [
@@ -3260,7 +3261,7 @@ class CTriggerExpressionTest extends TestCase {
* @param bool $options['allow_func_only']
*/
public function testParseExpression(string $expression, ?array $result, bool $rc, array $options = []) {
- $expression_data = new CTriggerExpression($options);
+ $expression_data = new C10TriggerExpression($options);
if ($expression_data->parse($expression)) {
$this->assertEquals($rc, true);
@@ -3282,28 +3283,28 @@ class CTriggerExpressionTest extends TestCase {
'((-12 + {host:item.str(ГУГЛ)} or {$USERMACRO} and not {TRIGGER.VALUE} or {#LLD} or {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")} or 10m))',
[
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
'value' => '(',
'data' => null,
'pos' => 0,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
'value' => '(',
'data' => null,
'pos' => 1,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => '-',
'data' => null,
'pos' => 2,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_NUMBER,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_NUMBER,
'value' => '12',
'pos' => 3,
'length' => 2,
@@ -3312,14 +3313,14 @@ class CTriggerExpressionTest extends TestCase {
]
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => '+',
'data' => null,
'pos' => 6,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO,
'value' => '{host:item.str(ГУГЛ)}',
'pos' => 8,
'length' => 25,
@@ -3328,81 +3329,93 @@ class CTriggerExpressionTest extends TestCase {
'item' => 'item',
'function' => 'str(ГУГЛ)',
'functionName' => 'str',
- 'functionParams' => ['ГУГЛ']
+ 'functionParams' => ['ГУГЛ'],
+ 'functionParamsRaw' => [
+ 'type' => C10FunctionParser::PARAM_ARRAY,
+ 'raw' => '(ГУГЛ)',
+ 'pos' => 3,
+ 'parameters' => [
+ [
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
+ 'raw' => 'ГУГЛ',
+ 'pos' => 1
+ ]
+ ]
+ ]
]
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 34,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_USER_MACRO,
'value' => '{$USERMACRO}',
'data' => null,
'pos' => 37,
'length' => 12
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'and',
'data' => null,
'pos' => 50,
'length' => 3
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'not',
'data' => null,
'pos' => 54,
'length' => 3
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_MACRO,
'value' => '{TRIGGER.VALUE}',
'data' => null,
'pos' => 58,
'length' => 15
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 74,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
'value' => '{#LLD}',
'data' => null,
'pos' => 77,
'length' => 6
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 84,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
'value' => '{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}',
'data' => null,
'pos' => 87,
'length' => 42
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 130,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_NUMBER,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_NUMBER,
'value' => '10m',
'pos' => 133,
'length' => 3,
@@ -3411,14 +3424,14 @@ class CTriggerExpressionTest extends TestCase {
]
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
'value' => ')',
'data' => null,
'pos' => 136,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
'value' => ')',
'data' => null,
'pos' => 137,
@@ -3427,24 +3440,24 @@ class CTriggerExpressionTest extends TestCase {
]
],
[
- '(-1.15w + str(host:item, ГУГЛ) or {$USERMACRO}) or {#LLD}',
+ '(-1.15w + str(host:item, "param2") or {$USERMACRO}) or {#LLD}',
[
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPEN_BRACE,
'value' => '(',
'data' => null,
'pos' => 0,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => '-',
'data' => null,
'pos' => 1,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_NUMBER,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_NUMBER,
'value' => '1.15w',
'pos' => 2,
'length' => 5,
@@ -3453,52 +3466,69 @@ class CTriggerExpressionTest extends TestCase {
]
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => '+',
'data' => null,
'pos' => 8,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_FUNCTION,
- 'value' => 'str(host:item, ГУГЛ)',
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION,
+ 'value' => 'str(host:item, "param2")',
'pos' => 10,
'length' => 24,
'data' => [
'functionName' => 'str',
- 'functionParams' => ['host:item', 'ГУГЛ']
+ 'functionParams' => ['host:item', 'param2'],
+ 'functionParamsRaw' => [
+ 'type' => C10FunctionParser::PARAM_ARRAY,
+ 'raw' => '(host:item, "param2")',
+ 'pos' => 3,
+ 'parameters' => [
+ [
+ 'type' => C10FunctionParser::PARAM_UNQUOTED,
+ 'raw' => 'host:item',
+ 'pos' => 1
+ ],
+ [
+ 'type' => C10FunctionParser::PARAM_QUOTED,
+ 'raw' => '"param2"',
+ 'pos' => 12
+ ]
+ ]
+ ]
]
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 35,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_USER_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_USER_MACRO,
'value' => '{$USERMACRO}',
'data' => null,
'pos' => 38,
'length' => 12
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_CLOSE_BRACE,
'value' => ')',
'data' => null,
'pos' => 50,
'length' => 1
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_OPERATOR,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_OPERATOR,
'value' => 'or',
'data' => null,
'pos' => 52,
'length' => 2
],
[
- 'type' => CTriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
+ 'type' => C10TriggerExprParserResult::TOKEN_TYPE_LLD_MACRO,
'value' => '{#LLD}',
'data' => null,
'pos' => 55,
@@ -3514,9 +3544,9 @@ class CTriggerExpressionTest extends TestCase {
* @dataProvider tokens_provider
*/
public function testTokens(string $expression, array $tokens, array $options = []) {
- $parser = new CTriggerExpression($options);
+ $parser = new C10TriggerExpression($options);
$result = $parser->parse($expression);
- $this->assertTrue($result instanceof CTriggerExprParserResult);
+ $this->assertTrue($result instanceof C10TriggerExprParserResult);
$this->assertEquals($tokens, $result->getTokens());
}
}
diff --git a/ui/tests/unit/include/classes/parsers/CExpressionMacroFunctionParserTest.php b/ui/tests/unit/include/classes/parsers/CExpressionMacroFunctionParserTest.php
new file mode 100644
index 00000000000..2ccce32ced7
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CExpressionMacroFunctionParserTest.php
@@ -0,0 +1,102 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CExpressionMacroFunctionParserTest extends TestCase {
+
+ public static function dataProvider() {
+ return [
+ ['', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['{{', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['{{?', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['text {{?}.f()}', 5, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['text {{?1+1}', 5, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['text {{?1+1}.func("param")}', 5, [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{?1+1}.func("param")}',
+ 'length' => 22
+ ]],
+ ['text {{?1+1}.func("param")} text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{{?1+1}.func("param")}',
+ 'length' => 22
+ ]],
+ ['text {{? 1 + 1 }.func( param, "param" )}', 5, [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{? 1 + 1 }.func( param, "param" )}',
+ 'length' => 37
+ ]],
+ ['text {{? last(/{HOST.HOST}/key, #25) }.func()} text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{{? last(/{HOST.HOST}/key, #25) }.func()}',
+ 'length' => 41
+ ]],
+ ['text {{? last(/host/key, #25) + max(sum(/host/key, 1d:now/d), sum(/host/key, 1d:now/d-1d)) }.func()} text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{{? last(/host/key, #25) + max(sum(/host/key, 1d:now/d), sum(/host/key, 1d:now/d-1d)) }.func()}',
+ 'length' => 95
+ ]],
+ ['text {? 1 + 1 text', 5, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $result
+ */
+ public function testExpressionMacroFunctionParser(string $source, int $pos, array $result) {
+ $expression_macro_function_parser = new CExpressionMacroFunctionParser();
+
+ $this->assertSame($result, [
+ 'rc' => $expression_macro_function_parser->parse($source, $pos),
+ 'match' => $expression_macro_function_parser->getMatch(),
+ 'length' => $expression_macro_function_parser->getLength()
+ ]);
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CExpressionMacroParserTest.php b/ui/tests/unit/include/classes/parsers/CExpressionMacroParserTest.php
new file mode 100644
index 00000000000..7bffc99d468
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CExpressionMacroParserTest.php
@@ -0,0 +1,107 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CExpressionMacroParserTest extends TestCase {
+
+ public static function dataProvider() {
+ return [
+ ['', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['{', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['{?', 0, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['text {?}', 5, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]],
+ ['text {?1+1}', 5, [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{?1+1}',
+ 'length' => 6
+ ]],
+ ['text {?1+1} text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{?1+1}',
+ 'length' => 6
+ ]],
+ ['text {? 1 + 1 }', 5, [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{? 1 + 1 }',
+ 'length' => 12
+ ]],
+ ['text {?last(/'.'/system.cpu.load)}', 5, [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{?last(/'.'/system.cpu.load)}',
+ 'length' => 26
+ ]],
+ ['text {? last(/{HOST.HOST}/key, #25) } text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{? last(/{HOST.HOST}/key, #25) }',
+ 'length' => 32
+ ]],
+ ['text {? last(/{HOST.HOST6}/key, #25) } text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{? last(/{HOST.HOST6}/key, #25) }',
+ 'length' => 33
+ ]],
+ ['text {? last(/host/key, #25) + max(sum(/host/key, 1d:now/d), sum(/host/key, 1d:now/d-1d)) } text', 5, [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{? last(/host/key, #25) + max(sum(/host/key, 1d:now/d), sum(/host/key, 1d:now/d-1d)) }',
+ 'length' => 86
+ ]],
+ ['text {? 1 + 1 text', 5, [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'length' => 0
+ ]]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $result
+ */
+ public function testExpressionMacroParser(string $source, int $pos, array $result) {
+ $expression_macro_parser = new CExpressionMacroParser();
+
+ $this->assertSame($result, [
+ 'rc' => $expression_macro_parser->parse($source, $pos),
+ 'match' => $expression_macro_parser->getMatch(),
+ 'length' => $expression_macro_parser->getLength()
+ ]);
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CExpressionParserTest.php b/ui/tests/unit/include/classes/parsers/CExpressionParserTest.php
new file mode 100644
index 00000000000..e8164cbe26b
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CExpressionParserTest.php
@@ -0,0 +1,2730 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CExpressionParserTest extends TestCase {
+
+ public static function dataProvider() {
+ return [
+ ['', ['error' => 'incorrect expression starting from ""', 'match' => ''], CParser::PARSE_FAIL],
+ [' ', ['error' => 'incorrect expression starting from ""', 'match' => ''], CParser::PARSE_FAIL],
+ ['+', ['error' => 'incorrect expression starting from "+"', 'match' => ''], CParser::PARSE_FAIL],
+ ['1+1', ['error' => '', 'match' => '1+1'], CParser::PARSE_SUCCESS],
+ ['1+1 ', null, CParser::PARSE_SUCCESS],
+ [' 1+1 '."\t\r\n", null, CParser::PARSE_SUCCESS],
+ ['abc', ['error' => 'incorrect expression starting from "abc"', 'match' => ''], CParser::PARSE_FAIL],
+ ['{#LLD}', ['error' => 'incorrect expression starting from "{#LLD}"', 'match' => ''], CParser::PARSE_FAIL],
+ ['{#LLD}', null, CParser::PARSE_SUCCESS, ['lldmacros' => true]],
+ ['{#LLD}', ['error' => 'incorrect expression starting from "{#LLD}"', 'match' => ''], CParser::PARSE_FAIL],
+
+ ['.5', null, CParser::PARSE_SUCCESS],
+ ['5.', null, CParser::PARSE_SUCCESS],
+ ['..5', null, CParser::PARSE_FAIL],
+ ['5..', ['error' => 'incorrect expression starting from "."', 'match' => '5.'], CParser::PARSE_SUCCESS_CONT],
+
+ ['1', null, CParser::PARSE_SUCCESS],
+ ['1s', null, CParser::PARSE_SUCCESS],
+ ['1m', null, CParser::PARSE_SUCCESS],
+ ['1h', null, CParser::PARSE_SUCCESS],
+ ['1d', null, CParser::PARSE_SUCCESS],
+ ['1w', null, CParser::PARSE_SUCCESS],
+ ['1K', null, CParser::PARSE_SUCCESS],
+ ['1M', null, CParser::PARSE_SUCCESS],
+ ['1G', null, CParser::PARSE_SUCCESS],
+ ['1T', null, CParser::PARSE_SUCCESS],
+ ['1.5', null, CParser::PARSE_SUCCESS],
+ ['1.5s', null, CParser::PARSE_SUCCESS],
+ ['1.5m', null, CParser::PARSE_SUCCESS],
+ ['1.5h', null, CParser::PARSE_SUCCESS],
+ ['1.5d', null, CParser::PARSE_SUCCESS],
+ ['1.5w', null, CParser::PARSE_SUCCESS],
+ ['1.5K', null, CParser::PARSE_SUCCESS],
+ ['1.5M', null, CParser::PARSE_SUCCESS],
+ ['1.5G', null, CParser::PARSE_SUCCESS],
+ ['1.5T', null, CParser::PARSE_SUCCESS],
+ ['-1.5', null, CParser::PARSE_SUCCESS],
+ ['-1.5s', null, CParser::PARSE_SUCCESS],
+ ['-1.5m', null, CParser::PARSE_SUCCESS],
+ ['-1.5h', null, CParser::PARSE_SUCCESS],
+ ['-1.5d', null, CParser::PARSE_SUCCESS],
+ ['-1.5w', null, CParser::PARSE_SUCCESS],
+ ['-1.5K', null, CParser::PARSE_SUCCESS],
+ ['-1.5M', null, CParser::PARSE_SUCCESS],
+ ['-1.5G', null, CParser::PARSE_SUCCESS],
+ ['-1.5T', null, CParser::PARSE_SUCCESS],
+
+ ['{TRIGGER.VALUE}', null, CParser::PARSE_SUCCESS],
+ ['{$USERMACRO}', null, CParser::PARSE_SUCCESS],
+ ['{TRIGGER.VALUE}=1', null, CParser::PARSE_SUCCESS],
+ ['{$USERMACRO}=1', null, CParser::PARSE_SUCCESS],
+
+ ['(/host)', null, CParser::PARSE_FAIL],
+ ['(/host/key)', null, CParser::PARSE_FAIL],
+
+ ['last(/host/key) and {TRIGGER.VALUE}', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and {TRIGGER.VALUE}', ['error' => 'incorrect expression starting from "and {TRIGGER.VALUE}"', 'match' => 'last(/host/key)'], CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and{TRIGGER.VALUE}', ['error' => 'incorrect expression starting from "{TRIGGER.VALUE}"', 'match' => 'last(/host/key)'], CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and + {TRIGGER.VALUE}', ['error' => 'incorrect expression starting from "+ {TRIGGER.VALUE}"', 'match' => 'last(/host/key)'], CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and - {TRIGGER.VALUE}', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key) and {$USERMACRO}', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and {$USERMACRO}', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and{$USERMACRO}', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and + {$USERMACRO}', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and - {$USERMACRO}', null, CParser::PARSE_SUCCESS],
+
+ // basic "not" support
+ ['not 1', null, CParser::PARSE_SUCCESS],
+ ['not (1)', null, CParser::PARSE_SUCCESS],
+ ['not -1', null, CParser::PARSE_SUCCESS],
+ ['not (-1)', null, CParser::PARSE_SUCCESS],
+ ['not -(1)', null, CParser::PARSE_SUCCESS],
+ ['not {TRIGGER.VALUE}', null, CParser::PARSE_SUCCESS],
+ ['not ({TRIGGER.VALUE})', null, CParser::PARSE_SUCCESS],
+ ['not -{TRIGGER.VALUE}', null, CParser::PARSE_SUCCESS],
+ ['not (-{TRIGGER.VALUE})', null, CParser::PARSE_SUCCESS],
+ ['not -({TRIGGER.VALUE})', null, CParser::PARSE_SUCCESS],
+ ['not {$USERMACRO}', null, CParser::PARSE_SUCCESS],
+ ['not ({$USERMACRO})', null, CParser::PARSE_SUCCESS],
+ ['not -{$USERMACRO}', null, CParser::PARSE_SUCCESS],
+ ['not (-{$USERMACRO})', null, CParser::PARSE_SUCCESS],
+ ['not -({$USERMACRO})', null, CParser::PARSE_SUCCESS],
+ ['not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['not (last(/host/key))', null, CParser::PARSE_SUCCESS],
+ ['not -last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['not (-last(/host/key))', null, CParser::PARSE_SUCCESS],
+ ['not -(last(/host/key))', null, CParser::PARSE_SUCCESS],
+
+ ['not1', null, CParser::PARSE_FAIL],
+ ['not(1)', null, CParser::PARSE_SUCCESS],
+ ['not-1', null, CParser::PARSE_FAIL],
+ ['not(-1)', null, CParser::PARSE_SUCCESS],
+ ['not-(1)', null, CParser::PARSE_FAIL],
+ ['not{TRIGGER.VALUE}', null, CParser::PARSE_FAIL],
+ ['not({TRIGGER.VALUE})', null, CParser::PARSE_SUCCESS],
+ ['not-{TRIGGER.VALUE}', null, CParser::PARSE_FAIL],
+ ['not(-{TRIGGER.VALUE})', null, CParser::PARSE_SUCCESS],
+ ['not-({TRIGGER.VALUE})', null, CParser::PARSE_FAIL],
+ ['not{$USERMACRO}', null, CParser::PARSE_FAIL],
+ ['not({$USERMACRO})', null, CParser::PARSE_SUCCESS],
+ ['not-{$USERMACRO}', null, CParser::PARSE_FAIL],
+ ['not(-{$USERMACRO})', null, CParser::PARSE_SUCCESS],
+ ['not-({$USERMACRO})', null, CParser::PARSE_FAIL],
+ ['not(last(/host/key))', null, CParser::PARSE_SUCCESS],
+ ['not(-last(/host/key))', null, CParser::PARSE_SUCCESS],
+ ['not-(last(/host/key))', null, CParser::PARSE_FAIL],
+
+ ['- not 1', null, CParser::PARSE_FAIL],
+ ['-not 1', null, CParser::PARSE_FAIL],
+ ['- not1', null, CParser::PARSE_FAIL],
+ ['-not1', null, CParser::PARSE_FAIL],
+ ['1 not 1', ['error' => 'incorrect expression starting from "not 1"', 'match' => '1'], CParser::PARSE_SUCCESS_CONT],
+ ['(1) not 1', ['error' => 'incorrect expression starting from "not 1"', 'match' => '(1)'], CParser::PARSE_SUCCESS_CONT],
+ ['1not1', ['error' => 'incorrect expression starting from "not1"', 'match' => '1'], CParser::PARSE_SUCCESS_CONT],
+ ['(1)not1', ['error' => 'incorrect expression starting from "not1"', 'match' => '(1)'], CParser::PARSE_SUCCESS_CONT],
+
+ // operator cases
+ ['Not 1', null, CParser::PARSE_FAIL],
+ ['NOT 1', null, CParser::PARSE_FAIL],
+ ['1 Or 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['1 OR 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['1 And 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['1 AND 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)=00', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=0 0', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=0 0=last(/host/key)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) = 00 = last(/host/key)', null, CParser::PARSE_SUCCESS],
+
+ ['count(/host/key[a,,"b",,[c,d,,"e",],,[f]],1,,"b",3)', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key[a,,"b",,[[c,d,,"e"],[]],,[f]],1,,"b",3)', null, CParser::PARSE_FAIL],
+ ['count(/host/key[a,,"b",,[c,d,,"e",,,[f]],1,,"b",3)', null, CParser::PARSE_FAIL],
+ ['count(/host/key[a,,"b",,[c,d,,"e",],,f]],1,,"b",3)', null, CParser::PARSE_FAIL],
+
+ ['last(/abcdefghijklmnopqrstuvwxyz. _-ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/abcdefghijklmnopqrstuvwxyz._-ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890)', null, CParser::PARSE_SUCCESS],
+ ['abcdefghijklmnopqrstuvwxyz(/host/key)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host)', null, CParser::PARSE_FAIL],
+ ['last(/host/,)', null, CParser::PARSE_FAIL],
+ ['last(/host/;)', null, CParser::PARSE_FAIL],
+ ['last(/host//)', null, CParser::PARSE_FAIL],
+ ['last(/host//)', null, CParser::PARSE_FAIL],
+
+ ['last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['(last(/host/key))', null, CParser::PARSE_SUCCESS],
+ ['(last(/host/key)', null, CParser::PARSE_FAIL],
+ ['((last(/host/key)', null, CParser::PARSE_FAIL],
+ ['last((/host/key))=0', null, CParser::PARSE_FAIL],
+ ['(last(/host/key)=)0', null, CParser::PARSE_FAIL],
+ ['(last(/host/key))0', ['error' => 'incorrect expression starting from "0"', 'match' => '(last(/host/key))'], CParser::PARSE_SUCCESS_CONT],
+ ['0(=last(/host/key))', null, CParser::PARSE_SUCCESS_CONT],
+ ['0(last(/host/key))', null, CParser::PARSE_SUCCESS_CONT],
+ ['( last(/host/key) )', null, CParser::PARSE_SUCCESS],
+ [' ( last(/host/key) ) ', null, CParser::PARSE_SUCCESS],
+ ['(( ( last(/host/key) ) ))', null, CParser::PARSE_SUCCESS],
+ [' ( ( ( last(/host/key) ) ) ) ', null, CParser::PARSE_SUCCESS],
+ ['((( ( last(/host/key) ) )))', null, CParser::PARSE_SUCCESS],
+ [' ( ( ( ( last(/host/key) ) ) ) ) ', null, CParser::PARSE_SUCCESS],
+ ['()0=( last(/host/key) )', null, CParser::PARSE_FAIL],
+ ['0()=( last(/host/key) )', ['error' => 'incorrect expression starting from "()=( last(/host/key) )"', 'match' => '0'], CParser::PARSE_SUCCESS_CONT],
+ ['0=()=( last(/host/key) )', null, CParser::PARSE_SUCCESS_CONT],
+ ['0=()( last(/host/key) )', null, CParser::PARSE_SUCCESS_CONT],
+ ['0=last(/host/key)()', null, CParser::PARSE_SUCCESS_CONT],
+ ['0=last(/host/key)+()()()()5', null, CParser::PARSE_SUCCESS_CONT],
+ ['0=last(/host/key)+((((()))))5', null, CParser::PARSE_SUCCESS_CONT],
+ ['(0)=last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['(0+)=last(/host/key)', null, CParser::PARSE_FAIL],
+ ['(0=)last(/host/key)', null, CParser::PARSE_FAIL],
+ ['(-5)=last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['(15 - 5.25 - 1)=last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = -5', null, CParser::PARSE_SUCCESS],
+ ['last(/0/0,0)=0', null, CParser::PARSE_SUCCESS],
+
+ ['((last(/host/key))=0)', null, CParser::PARSE_SUCCESS],
+ ['( ( last(/host/key) ) = 0 )', null, CParser::PARSE_SUCCESS],
+ ['((last(/host/key)) * 100) / 95', null, CParser::PARSE_SUCCESS],
+ ['((last(/host/key)) * 5.25K) / 95.0', null, CParser::PARSE_SUCCESS],
+ ['((last(/host/key)) * 1w) / 1d', null, CParser::PARSE_SUCCESS],
+ ['((last(/host/key)) * 1w) / 1Ks', ['error' => 'incorrect expression starting from "s"', 'match' => '((last(/host/key)) * 1w) / 1K'], CParser::PARSE_SUCCESS_CONT],
+ ['((last(/host/key)) * 1w) / (1d * (last(/host/key))', ['error' => 'incorrect expression starting from ""', 'match' => '((last(/host/key)) * 1w)'], CParser::PARSE_SUCCESS_CONT],
+ ['((last(/host/key)) * 1w) / (1d * last(/host/key))', ['error' => '', 'match' => '((last(/host/key)) * 1w) / (1d * last(/host/key))'], CParser::PARSE_SUCCESS],
+ ['((last(/host/key)) * 1w) / (1d * (last(/host/key)))', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or (-1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not (-1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)* (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and (-1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (-1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not (-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not (-1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not (-1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) -(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or(-1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not(-1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)*(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and(-1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(-1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ // "not(" is treated as math functionu name.
+ ['last(/host/key,1)*not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<not(-1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>not(-1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)andnot(-1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(-1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or -1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not -1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)* -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and -1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or -1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not -1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not -1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) --1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or-1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not-1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)--1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or-1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot-1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or (- 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not (- 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)* (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and (- 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (- 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not (- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not (- 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not (- 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) -(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or(- 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not(- 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)*(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and(- 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(- 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ // "not(" is treated as math functionu name.
+ ['last(/host/key,1)*not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<not(- 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>not(- 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)andnot(- 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(- 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or - 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not - 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)* - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and - 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or - 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not - 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not - 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) -- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or- 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot- 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or- 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot- 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or (+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not (+1)', ['error' => 'incorrect expression starting from "+1)"', 'match' => 'last(/host/key,1)'], CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot (+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot (+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or(+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot(+1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(+1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or +1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not +1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or +1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not +1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not +1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ++1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or+1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot+1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)++1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or+1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot+1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot+1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not (+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(+ 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or + 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not + 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or + 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not + 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not + 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ++ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or+ 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)++ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or+ 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot+ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot+ 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or (not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot (not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot (not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or(not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot(not1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(not1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not not1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not not1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnotnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornotnot1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>notnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnotnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornotnot1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or (not 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not (not 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)* (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and (not 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or (not 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not (not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not (not 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not (not 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) -(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or(not 1)', null, CParser::PARSE_SUCCESS],
+
+ // "not(" is treated as math functionu name.
+ ['last(/host/key,1) *not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) /not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) +not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) -not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) =not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <>not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) >not(not 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) andnot(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ // "not(" is treated as math functionu name.
+ ['last(/host/key,1)*not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)-not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)=not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<>not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<not(not 1)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)>not(not 1)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1)andnot(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot(not 1)', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) / not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) + not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) - not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) = not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) <> not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) < not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) > not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) and not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1) or not 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,1) * not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or not not 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)/ not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)+ not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)- not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)= not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)<> not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)< not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)> not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1)and not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)* not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/ not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+ not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)- not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)= not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<> not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)< not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)> not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)and not not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)or not not 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) * notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) / notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) + notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) - notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) = notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <> notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) < notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) > notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) and notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) or notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornot 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1)*notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)/notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)+notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)-notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)=notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<>notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)<notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)>notnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)andnotnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1)ornotnot 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key,1) *not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) /not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) +not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) -not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) =not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <>not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) <not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) >not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) andnot 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,1) ornot 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) + 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key) + not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or not 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)+ 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)/ 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)* 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)= 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)<> 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)or 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)+ not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)- not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)/ not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)* not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)= not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)<> not 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and not 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)or not 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) +1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) -1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) /1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) *1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) =1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <>1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) or1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) + not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) - not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) / not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) * not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) = not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) <> not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) or not1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)+1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)-1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)/1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)*1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)<>1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)or1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)+not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)/not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)*not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)<>not1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)andnot1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)ornot1', null, CParser::PARSE_SUCCESS_CONT],
+
+ // unary operators before binary operators
+ ['last(/host/key) -* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) -/ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -= 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -<> 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -and 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -or 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)-* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)-/ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-= 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-<> 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-and 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-or 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) -*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) --1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) -/1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -=1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -<>1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -and1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -or1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)-*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)--1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)-/1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-=1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-<>1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-and1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-or1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) not* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not/ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not= 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not<> 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) notand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) notor 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)not* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)not- 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)not/ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)not* 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)not= 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)not<> 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)notand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)notor 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) not*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not/1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not=1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not<>1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) notand1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) notor1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)-*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)--1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)-/1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-*1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-=1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-<>1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-and1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)-or1', null, CParser::PARSE_SUCCESS_CONT],
+
+ // suffixes
+ ['last(/host/key)=1K', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1M', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1G', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1T', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1s', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1m', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1h', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1d', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1w', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=1.56K', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56M', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56G', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56T', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56s', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56m', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56h', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56d', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56w', null, CParser::PARSE_SUCCESS],
+
+ // text operators after suffixes
+ ['last(/host/key)=1K and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1M and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1G and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1T and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1s and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1m and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1h and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1d and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1w and 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=1Kand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Mand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Gand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Tand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1sand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1mand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1hand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1dand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1wand 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)=1.56K and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56M and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56G and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56T and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56s and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56m and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56h and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56d and 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56w and 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=1.56Kand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Mand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Gand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Tand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56sand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56mand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56hand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56dand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56wand 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)=1K or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1M or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1G or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1T or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1s or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1m or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1h or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1d or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1w or 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=1Kor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Mor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Gor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1Tor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1sor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1mor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1hor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1dor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1wor 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)=1.56K or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56M or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56G or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56T or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56s or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56m or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56h or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56d or 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.56w or 1', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=1.56Kor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Mor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Gor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56Tor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56sor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56mor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56hor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56dor 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=1.56wor 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) + 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or 1.173640', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)+ 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)- 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)/ 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)* 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)= 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)<> 1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and 1.173640', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)or 1.173640', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) +1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) -1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) /1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) *1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) =1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <>1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and1.173640', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) or1.173640', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key)+1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)-1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)/1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)*1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)<>1.173640', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)and1.173640', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)or1.173640', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) + 1 or last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - 1 and last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / 1 <> last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * 1 = last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = 1 * last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> 1 / last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and 1 - last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or 1 + last(/host/key)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key) + not 1 or last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - not 1 and last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / not 1 <> last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * not 1 = last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = not 1 * last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> not 1 / last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and not 1 - last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or not 1 + last(/host/key)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key) + 1 or not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) - 1 and not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) / 1 <> not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) * 1 = not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) = 1 * not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) <> 1 / not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) and 1 - not last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) or 1 + not last(/host/key)', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key) -- 1', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key) ++ 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) // 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) ** 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) == 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) <><> 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) andand 1', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) oror 1', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) +', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) -', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) /', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) *', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) =', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) <>', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) or', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) not', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['last(/host/key) + not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) - not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) / not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) * not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) = not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) <> not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) and not', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key) or not', null, CParser::PARSE_SUCCESS_CONT],
+
+ ['- last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['+ last(/host/key)', null, CParser::PARSE_FAIL],
+ ['/ last(/host/key)', null, CParser::PARSE_FAIL],
+ ['* last(/host/key)', null, CParser::PARSE_FAIL],
+ ['= last(/host/key)', null, CParser::PARSE_FAIL],
+ ['<> last(/host/key)', null, CParser::PARSE_FAIL],
+ ['and last(/host/key)', null, CParser::PARSE_FAIL],
+ ['or last(/host/key)', null, CParser::PARSE_FAIL],
+
+ ['not - last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['not + last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not / last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not * last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not = last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not <> last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not and last(/host/key)', null, CParser::PARSE_FAIL],
+ ['not or last(/host/key)', null, CParser::PARSE_FAIL],
+
+ ['- not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['+ not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['/ not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['* not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['= not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['<> not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['and not last(/host/key)', null, CParser::PARSE_FAIL],
+ ['or not last(/host/key)', null, CParser::PARSE_FAIL],
+
+ // abs (expr)
+ ['abs(last(/host/item,#1)-last(/host/item,#2)) > 0', null, CParser::PARSE_SUCCESS],
+
+ // avg, min, max, sum (item, period OR expr1, ..., exprN)
+ ['avg(/host/item,#5) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,30s) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,10m) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,1h) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,#5:now-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,30m:now-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['avg(/host/item,1h:now/h) > 0', null, CParser::PARSE_SUCCESS],
+ ['min(/host/item,30m) > 0', null, CParser::PARSE_SUCCESS],
+ ['max(/host/item,30m) > 0', null, CParser::PARSE_SUCCESS],
+ ['sum(/host/item,30m) > 0', null, CParser::PARSE_SUCCESS],
+ ['sum(/host/item,60s) > 0', null, CParser::PARSE_SUCCESS],
+ ['sum(/host/item,60s:now-3600s) > 0', null, CParser::PARSE_SUCCESS],
+ ['sum(/host/item,1m:now-1h) > 0', null, CParser::PARSE_SUCCESS],
+
+ ['min(min(/host/key,1h),min(/host/key,1h),25)', null, CParser::PARSE_SUCCESS],
+ ['min(min(/host/key,1h),avg(/host/key,1h),25)', null, CParser::PARSE_SUCCESS],
+ ['min(min(/host/key,1h),avg(/host/key,1h)*100,25)', null, CParser::PARSE_SUCCESS],
+ ['min(min(/host1/key1,1h),min(/host2/key2,1h))', null, CParser::PARSE_SUCCESS],
+
+ // band (item, period, mask)
+ ['band(/host/item,#1,32) > 0', null, CParser::PARSE_SUCCESS],
+ ['band(/host/item,#2:now-1h,64) > 0', null, CParser::PARSE_SUCCESS],
+
+ // count (item, period, <operator>, <pattern>)
+ ['count(/host/item,#1,"eq","0") > 0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/item,5m:now-2h,"regexp","xyz") > 0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/item,5m:now-1h,"iregexp","10") > 0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/item,5m:now-2d,"gt",100) > 0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/item,1m,"band",32) > 0', null, CParser::PARSE_SUCCESS],
+
+ // date, dateofmonth, dayofweek, now, time ()
+ ['date() > 0', null, CParser::PARSE_SUCCESS],
+ ['dayofmonth() > 0', null, CParser::PARSE_SUCCESS],
+ ['dayofweek() > 0', null, CParser::PARSE_SUCCESS],
+ ['now() > 0', null, CParser::PARSE_SUCCESS],
+ ['time() > 0', null, CParser::PARSE_SUCCESS],
+
+ // forecast (item, period, time, <fit>, <mode>)
+ ['forecast(/host/item,#10,100s) > 0', null, CParser::PARSE_SUCCESS],
+ ['forecast(/host/item,3600s:now-7200s,600s,"linear","avg") > 0', null, CParser::PARSE_SUCCESS],
+ ['forecast(/host/item,30m:now-1d,600s,,"avg") > 0', null, CParser::PARSE_SUCCESS],
+
+ // fuzzytime (item, period)
+ ['fuzzytime(/host/item,60s) > 0', null, CParser::PARSE_SUCCESS],
+
+ // find (item, period, <operator>, <pattern>)
+ ['find(/host/key,#10,"iregexp","^error") > 0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,60s,"iregexp","^critical") > 0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,5m,"iregexp","^warning") > 0', null, CParser::PARSE_SUCCESS],
+
+ // last (item, period)
+ ['last(/host/key) > 0', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,#5) > 0', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,#10:now-3600s) > 0', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,#1:now-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['last(/host/vfs.fs.size[/,pfree])<10', null, CParser::PARSE_SUCCESS],
+ ['last(/host/vfs.fs.size["/var/log",pfree])<10', null, CParser::PARSE_SUCCESS],
+
+ // length (expr)
+ ['length(last(/host/key,30m)) > 0', null, CParser::PARSE_SUCCESS],
+ ['length(last(/host/key,60s)) > 0', null, CParser::PARSE_SUCCESS],
+ ['length(last(/host/key,#10)) > 0', null, CParser::PARSE_SUCCESS],
+ ['length(last(/host/key,60s:now-3600s)) > 0', null, CParser::PARSE_SUCCESS],
+ ['length(last(/host/key,1m:now-1h)) > 0', null, CParser::PARSE_SUCCESS],
+
+ // logeventid (item, <pattern>)
+ ['logeventid(/host/key,,"^error") > 0', null, CParser::PARSE_SUCCESS],
+
+ // logseverity (item)
+ ['logseverity(/host/key) > 0', null, CParser::PARSE_SUCCESS],
+
+ // logsource (item, <pattern>)
+ ['logsource(/host/item,,"^system$") > 0', null, CParser::PARSE_SUCCESS],
+
+ // nodata (item, period)
+ ['nodata(/host/item,60s) > 0', null, CParser::PARSE_SUCCESS],
+ ['nodata(/host/item,5m) > 0', null, CParser::PARSE_SUCCESS],
+
+ // percentile (item, period, percentage)
+ ['percentile(/host/key,30m,50) > 0', null, CParser::PARSE_SUCCESS],
+ ['percentile(/host/key,60s,60) > 0', null, CParser::PARSE_SUCCESS],
+ ['percentile(/host/key,#10,70) > 0', null, CParser::PARSE_SUCCESS],
+ ['percentile(/host/key,60s:now-3600s,80) > 0', null, CParser::PARSE_SUCCESS],
+ ['percentile(/host/key,1m:now-1h,90) > 0', null, CParser::PARSE_SUCCESS],
+
+ // timeleft (item, period, time, threshold, <fit>)
+ ['timeleft(/host/key,#10,100) > 0', null, CParser::PARSE_SUCCESS],
+ ['timeleft(/host/key,3600s:now-7200s,600,"linear") > 0', null, CParser::PARSE_SUCCESS],
+ ['timeleft(/host/key,30m:now-1d,600) > 0', null, CParser::PARSE_SUCCESS],
+
+ // trendavg, trendcount, trendmax, trendmin, trendsum (item, period)
+ ['trendavg(/host/item,1h:now/h-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['trendavg(/host/key,1M:now/M) > 1.2*trendavg(/host/key,1M:now/M-1y)', null, CParser::PARSE_SUCCESS],
+ ['trendcount(/host/item,1h:now/h-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['trendmax(/host/item,1h:now/h-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['trendmin(/host/item,1h:now/h-1d) > 0', null, CParser::PARSE_SUCCESS],
+ ['trendsum(/host/item,1h:now/h-1d) > 0', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key)=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1,)', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key, 1,)=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key, 1,)=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1, )=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1, )=0', null, CParser::PARSE_SUCCESS],
+
+ ['find(/host/key,,"like",")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like",""")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","""")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", "")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like", """)=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", """")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", "")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like", """)=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", """")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1,")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1,""")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"""")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1, ")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1, "")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1, """)=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1, """")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1, ")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1, "")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1, """)=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1, """")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1,"",")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"","")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1,"",""")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"","""")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1,"", ")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"", "")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1,"", """)=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"", """")=0', null, CParser::PARSE_FAIL],
+
+ ['count(/host/key,1,"", ")=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"", "")=0', null, CParser::PARSE_SUCCESS],
+ ['count(/host/key,1,"", """)=0', null, CParser::PARSE_FAIL],
+ ['count(/host/key,1,"", """")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like","\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","\"")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like","\\\\"")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like","\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","\""")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","\"""")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like",\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like",param\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like",param")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", \")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", \")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param\")=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param")=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like",()=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like",param()=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ()=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param()=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ()=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like", param()=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like",))=0', null, CParser::PARSE_SUCCESS_CONT],
+ ['find(/host/key,,"like",param))=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ))=0', null, CParser::PARSE_SUCCESS_CONT],
+ ['find(/host/key,,"like", param))=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like", ))=0', null, CParser::PARSE_SUCCESS_CONT],
+ ['find(/host/key,,"like", param))=0', null, CParser::PARSE_FAIL],
+
+ ['find(/host/key,,"like","(")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like","param(")=0', null, CParser::PARSE_SUCCESS],
+
+ ['find(/host/key,,"like",")")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like","param)")=0', null, CParser::PARSE_SUCCESS],
+
+ ['find(/host/key,,"like",)=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like", )=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like"," ")=0', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key,,"like",abc)=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like",\'abc\')=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key,,"like","")=0', null, CParser::PARSE_SUCCESS],
+
+ ['last(/host/key,0)=0', null, CParser::PARSE_SUCCESS],
+ ['(nodata(/hostA/keyA, "1h")=0) or (last(/hostB/keyB,123)=0)', null, CParser::PARSE_SUCCESS],
+ ['find(/host/key[asd[],aaa()=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key[asd[,asd[,[]],aaa()=0', null, CParser::PARSE_FAIL],
+ ['find(/host/key[[],[],[]])', null, CParser::PARSE_SUCCESS],
+
+ ['last(/hostkey,0)=0', null, CParser::PARSE_FAIL],
+ ['last(host/key,0)=0', null, CParser::PARSE_FAIL],
+ ['last(/host/key,0)=', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key,0)<>0', null, CParser::PARSE_SUCCESS],
+
+ ['(nodata(/hostA/keyA, "300s")=0) oror (last(/hostB/keyB,123)=0)', null, CParser::PARSE_SUCCESS_CONT],
+ ['(last(/host1/key1,0)/last(/host2/key2,#5))/10+2*{TRIGGER.VALUE} and {$USERMACRO1}+(-{$USERMACRO2})+-{$USERMACRO3}*-12K+12.5m', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,1.23)', null, CParser::PARSE_FAIL],
+ ['last(/host/key,1.23s)', null, CParser::PARSE_FAIL],
+ ['date()', null, CParser::PARSE_SUCCESS],
+ ['date(0)', null, CParser::PARSE_SUCCESS],
+ ['date(0,)', null, CParser::PARSE_FAIL],
+ ['dayofweek()', null, CParser::PARSE_SUCCESS],
+ ['dayofweek(0)', null, CParser::PARSE_SUCCESS],
+ ['dayofweek(0,)', null, CParser::PARSE_FAIL],
+ ['last(/host/key)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,0)', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key,#123)', null, CParser::PARSE_SUCCESS],
+ ['max(/host/key,123)', null, CParser::PARSE_SUCCESS],
+ ['now()', null, CParser::PARSE_SUCCESS],
+ ['now(0)', null, CParser::PARSE_SUCCESS],
+ ['now(0,)', null, CParser::PARSE_FAIL],
+ ['(--({host:key.last(0)}+K))', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TRIGGER', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TRIGGE', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TRIGG', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TRIG', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TRI', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{TR', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{T', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+{', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)+', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1)', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-1', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(-', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-(', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+-', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)+', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5)', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-5', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4-', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(4', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+(', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)+', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3)', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(3', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -(', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and -', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and ', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m and', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m an', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m a', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m ', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9m', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=9', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8=', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>8', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<>', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7<', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<7', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6<', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>6', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5>', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and 5', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and ', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 and', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 an', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 a', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4 ', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or 4', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or ', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 or', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 o', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3 ', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+3', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2+', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-2', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1-', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/1', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0/', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*0', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )*', null, CParser::PARSE_SUCCESS_CONT],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" )', null, CParser::PARSE_SUCCESS],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"" ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\""', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\"', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4\\', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p4', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", "', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3", ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3",', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3"', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p3', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, "', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1, ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1,', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #1', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], #', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ], ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ],', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ]', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"" ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\""', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\"', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4\\', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p4', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", "', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3", ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3",', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3"', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p3', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,"', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ,', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2 ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p2', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1, ', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1,', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p1', null, CParser::PARSE_FAIL],
+ ['func(/host/key[p', null, CParser::PARSE_FAIL],
+ ['func(/host/key[', null, CParser::PARSE_FAIL],
+ ['func(/host/key', null, CParser::PARSE_FAIL],
+ ['func(/host/ke', null, CParser::PARSE_FAIL],
+ ['func(/host/k', null, CParser::PARSE_FAIL],
+ ['func(/host/', null, CParser::PARSE_FAIL],
+ ['func(/host', null, CParser::PARSE_FAIL],
+ ['func(/hos', null, CParser::PARSE_FAIL],
+ ['func(/ho', null, CParser::PARSE_FAIL],
+ ['func(/h', null, CParser::PARSE_FAIL],
+ ['func(/', null, CParser::PARSE_FAIL],
+ ['func(', null, CParser::PARSE_FAIL],
+ ['func', null, CParser::PARSE_FAIL],
+ ['fun', null, CParser::PARSE_FAIL],
+ ['fu', null, CParser::PARSE_FAIL],
+ ['f', null, CParser::PARSE_FAIL],
+
+ // new lines and tabs
+ ["\rlast(/host/key,1)+1", null, CParser::PARSE_SUCCESS],
+ ["\nlast(/host/key,1)+1", null, CParser::PARSE_SUCCESS],
+ ["\r\nlast(/host/key,1)+1", null, CParser::PARSE_SUCCESS],
+ ["\tlast(/host/key,1)+1", null, CParser::PARSE_SUCCESS],
+
+ ["{\rhost:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{\nhost:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{\r\nhost:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{\thost:key.last(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host\r:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host\n:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host\r\n:key.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host\t:key.last(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host:\rkey.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:\nkey.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:\r\nkey.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:\tkey.last(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host:key\r.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key\n.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key\r\n.last(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key\t.last(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host:key.\rlast(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.\nlast(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.\r\nlast(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.\tlast(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host:key.last\r(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last\n(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last\r\n(1)}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last\t(1)}+1", null, CParser::PARSE_FAIL],
+
+ ["{host:key.last(1)\r}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last(1)\n}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last(1)\r\n}+1", null, CParser::PARSE_FAIL],
+ ["{host:key.last(1)\t}+1", null, CParser::PARSE_FAIL],
+
+ ["last(/host/key,1)\r+1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\n+1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\r\n+1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\t+1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)+\r1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+\r\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+\t1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)+1\r", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+1\n", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+1\r\n", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)+1\t", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)\r\r+\r\r1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\n\n+\n\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\r\n\r\n+\r\n\r\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\t\t+\t\t1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)\r\t+\r\t1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\n\t+\n\t1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\r\n\t+\r\n\t1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\t\t+\t\t1", null, CParser::PARSE_SUCCESS],
+
+ ["(\rlast(/host/key,1)+1) or 1", null, CParser::PARSE_SUCCESS],
+ ["(\nlast(/host/key,1)+1) or 1", null, CParser::PARSE_SUCCESS],
+ ["(\r\nlast(/host/key,1)+1) or 1", null, CParser::PARSE_SUCCESS],
+ ["(\tlast(/host/key,1)+1) or 1", null, CParser::PARSE_SUCCESS],
+
+ ["(last(/host/key,1)+1\r) or 1", null, CParser::PARSE_SUCCESS],
+ ["(last(/host/key,1)+1\n) or 1", null, CParser::PARSE_SUCCESS],
+ ["(last(/host/key,1)+1\r\n) or 1", null, CParser::PARSE_SUCCESS],
+ ["(last(/host/key,1)+1\t) or 1", null, CParser::PARSE_SUCCESS],
+ ["(last(/host/key,1)+1\t) or 1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)\ror not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\nor not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\r\nor not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\tor not 1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1) or\rnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) or\nnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) or\r\nnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) or\tnot 1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1)\rand not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\nand not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\r\nand not 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1)\tand not 1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1) and\rnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and\nnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and\r\nnot 1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and\tnot 1", null, CParser::PARSE_SUCCESS],
+
+ ["last(/host/key,1) and not\r1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and not\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and not\r\n1", null, CParser::PARSE_SUCCESS],
+ ["last(/host/key,1) and not\t1", null, CParser::PARSE_SUCCESS],
+
+ ["count(/host/key,1,\"\"\r)=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\"\"\n)=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\"\"\r\n)=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\"\"\t)=0", null, CParser::PARSE_FAIL],
+
+ ["count(/host/key,1,\"\r\")=0", null, CParser::PARSE_SUCCESS],
+ ["count(/host/key,1,\"\n\")=0", null, CParser::PARSE_SUCCESS],
+ ["count(/host/key,1,\"\r\n\")=0", null, CParser::PARSE_SUCCESS],
+ ["count(/host/key,1,\"\t\")=0", null, CParser::PARSE_SUCCESS],
+
+ ["count(/host/key,1,\r\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\n\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\r\n\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1,\t\"\")=0", null, CParser::PARSE_FAIL],
+
+ ["count(/host/key,1\r,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1\n,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1\r\n,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,1\t,\"\")=0", null, CParser::PARSE_FAIL],
+
+ ["count(/host/key,\r1,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,\n1,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,\r\n1,\"\")=0", null, CParser::PARSE_FAIL],
+ ["count(/host/key,\t1,\"\")=0", null, CParser::PARSE_FAIL],
+
+ // collapsed trigger expressions
+ ['func(/host/key)', null, CParser::PARSE_FAIL, ['collapsed_expression' => true]],
+ ['{123}', null, CParser::PARSE_SUCCESS, ['collapsed_expression' => true]],
+ ['{123} = {$MACRO}', null, CParser::PARSE_SUCCESS, ['collapsed_expression' => true]],
+ ['{123} = {#MACRO}', null, CParser::PARSE_SUCCESS, ['collapsed_expression' => true, 'lldmacros' => true]],
+ ['{123} = {#MACRO}', null, CParser::PARSE_SUCCESS_CONT, ['collapsed_expression' => true]],
+
+ // Compare strings.
+ ['last(/host/key)=""', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)=" "', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)="\"abc\""', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)="\"a\\bc\""', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)= "\"abc" ', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)="\\\"', null, CParser::PARSE_SUCCESS], // Actually looks like last(/host/key)="\\"
+ ['last(/host/key)="\\ \""', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=" " ', null, CParser::PARSE_SUCCESS],
+ ['"abc"="abc"', null, CParser::PARSE_SUCCESS],
+ [' "abc" = "abc" ', null, CParser::PARSE_SUCCESS],
+ ['"abc"="abc"="abc"', null, CParser::PARSE_SUCCESS],
+ ['"abc"="abc" and "abc"', null, CParser::PARSE_SUCCESS],
+ ['last(/host/key)="\ "', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\\', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\"', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=""abc', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)=" "abc', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="abc\"', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\""\"', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\\ \"', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\\\\\ "', null, CParser::PARSE_SUCCESS_CONT], // Actually looks like last(/host/key)="\\\ "
+ ['last(/host/key)=" " "', null, CParser::PARSE_SUCCESS_CONT],
+ ['last(/host/key)="\n"', null, CParser::PARSE_SUCCESS_CONT],
+ ['"abc"="abc"and"abc"', null, CParser::PARSE_SUCCESS_CONT],
+ ['"abc"="abc" and abc"', null, CParser::PARSE_SUCCESS_CONT],
+ ['min(last(/{HOST.HOST}/key), 1)', null, CParser::PARSE_FAIL],
+ ['min(last(/{HOST.HOST9}/key), 1)', null, CParser::PARSE_FAIL, ['host_macro' => true]],
+ ['min(last(/{HOST.HOST}/key), 1)', null, CParser::PARSE_SUCCESS, ['host_macro' => true]],
+ ['min(last(/{HOST.HOST}/key), 1)', null, CParser::PARSE_SUCCESS, ['host_macro_n' => true]],
+ ['min(last(/{HOST.HOST2}/key), 1)', null, CParser::PARSE_SUCCESS, ['host_macro_n' => true]],
+
+ ['last(/*/agent.ping) = 1 or last(/host2/*) = 1 or last(/*/*)', null, CParser::PARSE_SUCCESS, ['calculated' => true]],
+ ['last(/'.'/agent.ping) = 1', null, CParser::PARSE_FAIL, ['calculated' => true]],
+ ['last(/'.'/agent.ping) = 1', null, CParser::PARSE_SUCCESS, ['empty_host' => true]],
+ ['last(/'.'/*) = 1', null, CParser::PARSE_FAIL, ['calculated' => true]],
+ ['last(/'.'/*) = 1', null, CParser::PARSE_FAIL, ['empty_host' => true]],
+ ['last(/'.'/*) = 1', null, CParser::PARSE_SUCCESS, ['calculated' => true, 'empty_host' => true]],
+ ['last(/*/agent.ping) = 1 or last(/host2/*?[group = "Zabbix servers" and (tag = "tag1" or tag = "tag2")]) = 1 or last(/*/*)', null, CParser::PARSE_SUCCESS, ['calculated' => true]],
+ ['last(/*/agent.ping) = 1 or last(/host2/*?[group = "Zabbix servers" and (tag = {$MACRO} or tag = "tag2")]) = 1 or last(/*/*)', null, CParser::PARSE_SUCCESS, ['calculated' => true]],
+ ['last(/host2/*?[group = "Zabbix servers" and (tag = {#MACRO} or tag = "tag2")]) = 1', null, CParser::PARSE_FAIL, ['calculated' => true]],
+ ['last(/host2/*?[group = "Zabbix servers" and (tag = {#MACRO} or tag = {{#MACRO}.func()})]) = 1', null, CParser::PARSE_SUCCESS, ['lldmacros' => true, 'calculated' => true]],
+ ['last(/*/agent.ping) = 1 or last(/host2/*) = 1 or last(/*/*) or last(/{HOST.HOST}/key)', ['error' => 'incorrect expression starting from "last(/{HOST.HOST}/key)"', 'match' => 'last(/*/agent.ping) = 1 or last(/host2/*) = 1 or last(/*/*)'], CParser::PARSE_SUCCESS_CONT, ['calculated' => true]],
+ ['last(/*/agent.ping) = 1 or last(/host2/*) = 1 or last(/*/*) or last(/{HOST.HOST}/key)', null, CParser::PARSE_SUCCESS, ['calculated' => true, 'host_macro' => true]],
+ ['last(/*/agent.ping) = {TRIGGER.VALUE}', ['error' => 'incorrect expression starting from "{TRIGGER.VALUE}"', 'match' => 'last(/*/agent.ping)'], CParser::PARSE_SUCCESS_CONT, ['calculated' => true]],
+ ['last(/*/agent.ping) = 1 or last(/host2/*) = 1', null, CParser::PARSE_FAIL]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $expression
+ * @param array|null $result
+ * @param int $rc
+ * @param array $options
+ * @param bool $options['lldmacros']
+ * @param bool $options['collapsed_expression']
+ * @param bool $options['calculated']
+ * @param bool $options['host_macro']
+ */
+ public function testParseExpression(string $expression, ?array $result, int $rc, array $options = []) {
+ $expression_parser = new CExpressionParser($options);
+
+ $this->assertSame($rc, $expression_parser->parse($expression));
+
+ if ($result !== null) {
+ $this->assertSame($result, [
+ 'error' => $expression_parser->getError(),
+ 'match' => $expression_parser->getMatch()
+ ]);
+ $this->assertSame(strlen($result['match']), $expression_parser->getLength());
+ }
+ }
+
+ public static function dataProviderTokens() {
+ return [
+ [
+ '((-12 + {$MACRO})) = 1K or not {{#M}.regsub("^([0-9]+)", \1)} and {TRIGGER.VALUE} and "\\"str\\"" = func(/host/key, #25:now/M, "eq", "str") or math() or min( last(/host/key), {$MACRO}, 123, "abc" , min(min(/host/key, 1d:now/d), 125) + 10 )',
+ [
+ 'match' => '((-12 + {$MACRO})) = 1K or not {{#M}.regsub("^([0-9]+)", \1)} and {TRIGGER.VALUE} and "\\"str\\"" = func(/host/key, #25:now/M, "eq", "str") or math() or min( last(/host/key), {$MACRO}, 123, "abc" , min(min(/host/key, 1d:now/d), 125) + 10 )',
+ 'length' => 237,
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => 0,
+ 'match' => '(',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => 1,
+ 'match' => '(',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 2,
+ 'match' => '-',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 3,
+ 'match' => '12',
+ 'length' => 2,
+ 'data' => [
+ 'suffix' => null
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 6,
+ 'match' => '+',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 8,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => 16,
+ 'match' => ')',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => 17,
+ 'match' => ')',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 19,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 21,
+ 'match' => '1K',
+ 'length' => 2,
+ 'data' => [
+ 'suffix' => 'K'
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 24,
+ 'match' => 'or',
+ 'length' => 2
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 27,
+ 'match' => 'not',
+ 'length' => 3
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_LLD_MACRO,
+ 'pos' => 31,
+ 'match' => '{{#M}.regsub("^([0-9]+)", \1)}',
+ 'length' => 30
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 62,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MACRO,
+ 'pos' => 66,
+ 'match' => '{TRIGGER.VALUE}',
+ 'length' => 15
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 82,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_STRING,
+ 'pos' => 86,
+ 'match' => '"\\"str\\""',
+ 'length' => 9
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 96,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION,
+ 'pos' => 98,
+ 'match' => 'func(/host/key, #25:now/M, "eq", "str")',
+ 'length' => 39,
+ 'data' => [
+ 'function' => 'func',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 103,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 114,
+ 'match' => '#25:now/M',
+ 'length' => 9,
+ 'data' => [
+ 'sec_num' => '#25',
+ 'time_shift' => 'now/M'
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUOTED,
+ 'pos' => 125,
+ 'match' => '"eq"',
+ 'length' => 4
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUOTED,
+ 'pos' => 131,
+ 'match' => '"str"',
+ 'length' => 5
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 138,
+ 'match' => 'or',
+ 'length' => 2
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ 'pos' => 141,
+ 'match' => 'math()',
+ 'length' => 6,
+ 'data' => [
+ 'function' => 'math',
+ 'parameters' => []
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 148,
+ 'match' => 'or',
+ 'length' => 2
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ 'pos' => 151,
+ 'match' => 'min( last(/host/key), {$MACRO}, 123, "abc" , min(min(/host/key, 1d:now/d), 125) + 10 )',
+ 'length' => 86,
+ 'data' => [
+ 'function' => 'min',
+ 'parameters' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 156,
+ 'match' => 'last(/host/key)',
+ 'length' => 15,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION,
+ 'pos' => 156,
+ 'match' => 'last(/host/key)',
+ 'length' => 15,
+ 'data' => [
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 161,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 173,
+ 'match' => '{$MACRO}',
+ 'length' => 8,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 173,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 183,
+ 'match' => '123',
+ 'length' => 3,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 183,
+ 'match' => '123',
+ 'length' => 3,
+ 'data' => [
+ 'suffix' => null
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 188,
+ 'match' => '"abc"',
+ 'length' => 5,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_STRING,
+ 'pos' => 188,
+ 'match' => '"abc"',
+ 'length' => 5
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 196,
+ 'match' => 'min(min(/host/key, 1d:now/d), 125) + 10',
+ 'length' => 39,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ 'pos' => 196,
+ 'match' => 'min(min(/host/key, 1d:now/d), 125)',
+ 'length' => 34,
+ 'data' => [
+ 'function' => 'min',
+ 'parameters' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 200,
+ 'match' => 'min(/host/key, 1d:now/d)',
+ 'length' => 24,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION,
+ 'pos' => 200,
+ 'match' => 'min(/host/key, 1d:now/d)',
+ 'length' => 24,
+ 'data' => [
+ 'function' => 'min',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 204,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 215,
+ 'match' => '1d:now/d',
+ 'length' => 8,
+ 'data' => [
+ 'sec_num' => '1d',
+ 'time_shift' => 'now/d'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 226,
+ 'match' => '125',
+ 'length' => 3,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 226,
+ 'match' => '125',
+ 'length' => 3,
+ 'data' => [
+ 'suffix' => null
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 231,
+ 'match' => '+',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 233,
+ 'match' => '10',
+ 'length' => 2,
+ 'data' => [
+ 'suffix' => null
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['usermacros' => true, 'lldmacros' => true]
+ ],
+ [
+ '{52735} > 0 or min({52736}, {52737}, 5M + {$MACRO})',
+ [
+ 'match' => '{52735} > 0 or min({52736}, {52737}, 5M + {$MACRO})',
+ 'length' => 51,
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO,
+ 'pos' => 0,
+ 'match' => '{52735}',
+ 'length' => 7
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 8,
+ 'match' => '>',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 10,
+ 'match' => '0',
+ 'length' => 1,
+ 'data' => [
+ 'suffix' => null
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 12,
+ 'match' => 'or',
+ 'length' => 2
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_MATH_FUNCTION,
+ 'pos' => 15,
+ 'match' => 'min({52736}, {52737}, 5M + {$MACRO})',
+ 'length' => 36,
+ 'data' => [
+ 'function' => 'min',
+ 'parameters' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 19,
+ 'match' => '{52736}',
+ 'length' => 7,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO,
+ 'pos' => 19,
+ 'match' => '{52736}',
+ 'length' => 7
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 28,
+ 'match' => '{52737}',
+ 'length' => 7,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_FUNCTIONID_MACRO,
+ 'pos' => 28,
+ 'match' => '{52737}',
+ 'length' => 7
+ ]
+ ]
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_EXPRESSION,
+ 'pos' => 37,
+ 'match' => '5M + {$MACRO}',
+ 'length' => 13,
+ 'data' => [
+ 'tokens' => [
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_NUMBER,
+ 'pos' => 37,
+ 'match' => '5M',
+ 'length' => 2,
+ 'data' => [
+ 'suffix' => 'M'
+ ]
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_OPERATOR,
+ 'pos' => 40,
+ 'match' => '+',
+ 'length' => 1
+ ],
+ [
+ 'type' => CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 42,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['collapsed_expression' => true]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderTokens
+ */
+ public function testTokens(string $expression, array $expected, array $options = []) {
+ $expression_parser = new CExpressionParser($options);
+ $this->assertSame(CParser::PARSE_SUCCESS, $expression_parser->parse($expression));
+ $this->assertSame($expected, [
+ 'match' => $expression_parser->getMatch(),
+ 'length' => $expression_parser->getLength(),
+ 'tokens' => $expression_parser->getResult()->getTokens()
+ ]);
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CFilterParserTest.php b/ui/tests/unit/include/classes/parsers/CFilterParserTest.php
new file mode 100644
index 00000000000..e758a27c45b
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CFilterParserTest.php
@@ -0,0 +1,379 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CFilterParserTest extends TestCase {
+
+ public function dataProvider() {
+ return [
+ [
+ '?[tag="name"]', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?[tag="name"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 2,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 5,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 6,
+ 'match' => '"name"',
+ 'length' => 6
+ ]
+ ]
+ ]
+ ],
+ [
+ '?[ group = "\\"string1\\"" and tag = "\\"string2\\"" ]', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?[ group = "\"string1\"" and tag = "\"string2\"" ]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 3,
+ 'match' => 'group',
+ 'length' => 5
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 9,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 11,
+ 'match' => '"\"string1\""',
+ 'length' => 13
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 25,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 29,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 33,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 35,
+ 'match' => '"\"string2\""',
+ 'length' => 13
+ ]
+ ]
+ ]
+ ],
+ [
+ '?[((tag="tag1" or group="name") and tag="tag2")]', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?[((tag="tag1" or group="name") and tag="tag2")]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => 2,
+ 'match' => '(',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPEN_BRACE,
+ 'pos' => 3,
+ 'match' => '(',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 4,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 7,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 8,
+ 'match' => '"tag1"',
+ 'length' => 6
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 15,
+ 'match' => 'or',
+ 'length' => 2
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 18,
+ 'match' => 'group',
+ 'length' => 5
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 23,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 24,
+ 'match' => '"name"',
+ 'length' => 6
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => 30,
+ 'match' => ')',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 32,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 36,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 39,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 40,
+ 'match' => '"tag2"',
+ 'length' => 6
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_CLOSE_BRACE,
+ 'pos' => 46,
+ 'match' => ')',
+ 'length' => 1
+ ]
+ ]
+ ]
+ ],
+ [
+ '?[tag="name"] text', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '?[tag="name"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 2,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 5,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 6,
+ 'match' => '"name"',
+ 'length' => 6
+ ]
+
+ ]
+ ]
+ ],
+ [
+ '?["string1" = "string2"]', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?["string1" = "string2"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 2,
+ 'match' => '"string1"',
+ 'length' => 9
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 12,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 14,
+ 'match' => '"string2"',
+ 'length' => 9
+ ]
+
+ ]
+ ]
+ ],
+ [
+ '?["{$MACRO}" = "{#MACRO}"]', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?["{$MACRO}" = "{#MACRO}"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 2,
+ 'match' => '"{$MACRO}"',
+ 'length' => 10
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 13,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 15,
+ 'match' => '"{#MACRO}"',
+ 'length' => 10
+ ]
+ ]
+ ]
+ ],
+ [
+ '?[{$MACRO} <> {#MACRO}]', 0, ['usermacros' => true, 'lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '?[{$MACRO} <> {#MACRO}]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 2,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 11,
+ 'match' => '<>',
+ 'length' => 2
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_LLD_MACRO,
+ 'pos' => 14,
+ 'match' => '{#MACRO}',
+ 'length' => 8
+ ]
+ ]
+ ]
+ ],
+ [
+ '?[{$MACRO} = {#MACRO}]', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ],
+ [
+ '?[{$MACRO} = {#MACRO}]', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ],
+ [
+ '?[{$MACRO} = {#MACRO}]', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ],
+ [
+ '?[tag=tag]', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ],
+ [
+ '?[()]', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ],
+ [
+ '?[(tag = "tag"]', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ */
+ public function testFilterParser($source, $pos, $options, $expected) {
+ $filter_parser = new CFilterParser($options);
+
+ $this->assertSame($expected, [
+ 'rc' => $filter_parser->parse($source, $pos),
+ 'match' => $filter_parser->getMatch(),
+ 'tokens' => $filter_parser->getTokens()
+ ]);
+ $this->assertSame(strlen($expected['match']), $filter_parser->getLength());
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CHistFunctionParserTest.php b/ui/tests/unit/include/classes/parsers/CHistFunctionParserTest.php
new file mode 100644
index 00000000000..60db7e06399
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CHistFunctionParserTest.php
@@ -0,0 +1,1201 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CHistFunctionParserTest extends TestCase {
+
+ /**
+ * An array of trigger functions and parsed results.
+ */
+ public static function dataProvider() {
+ return [
+ [
+ 'last(/host/key)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/host/key']
+ ],
+ [
+ 'last(/{HOST.HOST}/key)', 0, ['host_macro' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/{HOST.HOST}/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/{HOST.HOST}/key',
+ 'length' => 16,
+ 'data' => [
+ 'host' => '{HOST.HOST}',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/{HOST.HOST}/key']
+ ],
+ [
+ 'last(/{HOST.HOST}/key)', 0, ['host_macro_n' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/{HOST.HOST}/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/{HOST.HOST}/key',
+ 'length' => 16,
+ 'data' => [
+ 'host' => '{HOST.HOST}',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/{HOST.HOST}/key']
+ ],
+ [
+ 'last(/{HOST.HOST3}/key)', 0, ['host_macro_n' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/{HOST.HOST3}/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/{HOST.HOST3}/key',
+ 'length' => 17,
+ 'data' => [
+ 'host' => '{HOST.HOST3}',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/{HOST.HOST3}/key']
+ ],
+ [
+ '{$A} = 5 or last(/host/key)', 12, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 17,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/host/key']
+ ],
+ [
+ 'last( /host/key )', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last( /host/key )',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 7,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/host/key']
+ ],
+ [
+ 'last( /host/key[ "param1", param2, "param3" ,"param4\""] )', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last( /host/key[ "param1", param2, "param3" ,"param4\""] )',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 6,
+ 'match' => '/host/key[ "param1", param2, "param3" ,"param4\""]',
+ 'length' => 50,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key[ "param1", param2, "param3" ,"param4\""]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/host/key[ "param1", param2, "param3" ,"param4\""]']
+ ],
+ [
+ 'last(/host/*)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/host/*)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/*)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/*',
+ 'length' => 7,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/host/*']
+ ],
+ [
+ 'last(/*/key)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/*/key)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/*/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/*/key',
+ 'length' => 6,
+ 'data' => [
+ 'host' => '*',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/*/key']
+ ],
+ [
+ 'last(/'.'/key)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/'.'/key)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/'.'/key)', 0, ['empty_host' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/'.'/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/'.'/key',
+ 'length' => 5,
+ 'data' => [
+ 'host' => '',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/'.'/key']
+ ],
+ [
+ 'last(/'.'/*)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/'.'/*)', 0, ['calculated' => true, 'empty_host' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/'.'/*)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/'.'/*',
+ 'length' => 3,
+ 'data' => [
+ 'host' => '',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/'.'/*']
+ ],
+ [
+ 'last(/*/*)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/*/*)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/*/*)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/*/*',
+ 'length' => 4,
+ 'data' => [
+ 'host' => '*',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/*/*']
+ ],
+ [
+ 'sum(/host/key?[tag="a" and not tag="b"], 1m)', 0, ['calculated' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'sum(/host/key?[tag="a" and not tag="b"], 1m)',
+ 'function' => 'sum',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 4,
+ 'match' => '/host/key?[tag="a" and not tag="b"]',
+ 'length' => 35,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '?[tag="a" and not tag="b"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 15,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 18,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 19,
+ 'match' => '"a"',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 23,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 27,
+ 'match' => 'not',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 31,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 34,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 35,
+ 'match' => '"b"',
+ 'length' => 3
+ ]
+ ]
+ ]
+ ]
+ ],
+ 1 => [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 41,
+ 'match' => '1m',
+ 'length' => 2,
+ 'data' => [
+ 'sec_num' => '1m',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key?[tag="a" and not tag="b"]', '1m']
+ ],
+ [
+ 'sum(/host/key?[tag={$MACRO} and not tag={#MACRO}], 1m)', 0, ['usermacros' => true, 'lldmacros' => true, 'calculated' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'sum(/host/key?[tag={$MACRO} and not tag={#MACRO}], 1m)',
+ 'function' => 'sum',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 4,
+ 'match' => '/host/key?[tag={$MACRO} and not tag={#MACRO}]',
+ 'length' => 45,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '?[tag={$MACRO} and not tag={#MACRO}]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 15,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 18,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 19,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 28,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 32,
+ 'match' => 'not',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 36,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 39,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_LLD_MACRO,
+ 'pos' => 40,
+ 'match' => '{#MACRO}',
+ 'length' => 8
+ ]
+ ]
+ ]
+ ]
+ ],
+ 1 => [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 51,
+ 'match' => '1m',
+ 'length' => 2,
+ 'data' => [
+ 'sec_num' => '1m',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key?[tag={$MACRO} and not tag={#MACRO}]', '1m']
+ ],
+ [
+ 'sum(/host/key?[tag={$MACRO} and not tag="b"], 1m)', 0, ['lldmacros' => true, 'calculated' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'sum(/host/key?[tag={#MACRO} and not tag="b"], 1m)', 0, ['usermacros' => true, 'calculated' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/{HOST.HOST}/key)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/{HOST.HOST5}/key)', 0, ['host_macro' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/{HOST.HOST}/key)', 0, ['host_macro' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/{HOST.HOST}/key)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/{HOST.HOST}/key',
+ 'length' => 16,
+ 'data' => [
+ 'host' => '{HOST.HOST}',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['/{HOST.HOST}/key']
+ ],
+ [
+ 'last(/host/key, #25)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, #25)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '#25',
+ 'length' => 3,
+ 'data' => [
+ 'sec_num' => '#25',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '#25']
+ ],
+ [
+ 'last(/host/key, 25)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, 25)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '25',
+ 'length' => 2,
+ 'data' => [
+ 'sec_num' => '25',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '25']
+ ],
+ [
+ 'last(/host/key, 10h)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, 10h)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '10h',
+ 'length' => 3,
+ 'data' => [
+ 'sec_num' => '10h',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '10h']
+ ],
+ [
+ 'last(/host/key, 1h:now/d-1h)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, 1h:now/d-1h)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '1h:now/d-1h',
+ 'length' => 11,
+ 'data' => [
+ 'sec_num' => '1h',
+ 'time_shift' => 'now/d-1h'
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '1h:now/d-1h']
+ ],
+ [
+ 'last(/host/key,)', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key,)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 15,
+ 'match' => '',
+ 'length' => 0
+ ]
+ ]
+ ],
+ ['/host/key', '']
+ ],
+ [
+ 'last(/host/key, {$PERIOD}:{$OFFSET})', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, {$PERIOD}:{$OFFSET})',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '{$PERIOD}:{$OFFSET}',
+ 'length' => 19,
+ 'data' => [
+ 'sec_num' => '{$PERIOD}',
+ 'time_shift' => '{$OFFSET}'
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '{$PERIOD}:{$OFFSET}']
+ ],
+ [
+ 'last(/host/key, {$PERIOD}:now-{$ONE_HOUR} )', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, {$PERIOD}:now-{$ONE_HOUR} )',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '{$PERIOD}:now-{$ONE_HOUR}',
+ 'length' => 25,
+ 'data' => [
+ 'sec_num' => '{$PERIOD}',
+ 'time_shift' => 'now-{$ONE_HOUR}'
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '{$PERIOD}:now-{$ONE_HOUR}']
+ ],
+ [
+ 'last(/host/key, {$PERIOD} )', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, {$PERIOD} )',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '{$PERIOD}',
+ 'length' => 9,
+ 'data' => [
+ 'sec_num' => '{$PERIOD}',
+ 'time_shift' => ''
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '{$PERIOD}']
+ ],
+ [
+ 'last(/host/key, {{#PERIOD}.regsub("^([0-9]+)", \1)}:now/{#MONTH}-{#ONE_HOUR} )', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, {{#PERIOD}.regsub("^([0-9]+)", \1)}:now/{#MONTH}-{#ONE_HOUR} )',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '{{#PERIOD}.regsub("^([0-9]+)", \1)}:now/{#MONTH}-{#ONE_HOUR}',
+ 'length' => 60,
+ 'data' => [
+ 'sec_num' => '{{#PERIOD}.regsub("^([0-9]+)", \1)}',
+ 'time_shift' => 'now/{#MONTH}-{#ONE_HOUR}'
+ ]
+ ]
+ ]
+ ],
+ ['/host/key', '{{#PERIOD}.regsub("^([0-9]+)", \1)}:now/{#MONTH}-{#ONE_HOUR}']
+ ],
+ [
+ 'last(/host/key, #25, "abc" ,"\"def\"", 1, 1.125, -1e12, {$M} , {$M: context}, {#M}, {{#M}.regsub()},, ,)', 0, ['usermacros' => true, 'lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'last(/host/key, #25, "abc" ,"\"def\"", 1, 1.125, -1e12, {$M} , {$M: context}, {#M}, {{#M}.regsub()},, ,)',
+ 'function' => 'last',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 5,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_PERIOD,
+ 'pos' => 16,
+ 'match' => '#25',
+ 'length' => 3,
+ 'data' => [
+ 'sec_num' => '#25',
+ 'time_shift' => ''
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUOTED,
+ 'pos' => 21,
+ 'match' => '"abc"',
+ 'length' => 5
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUOTED,
+ 'pos' => 28,
+ 'match' => '"\"def\""',
+ 'length' => 9
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 39,
+ 'match' => '1',
+ 'length' => 1
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 42,
+ 'match' => '1.125',
+ 'length' => 5
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 49,
+ 'match' => '-1e12',
+ 'length' => 5
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 56,
+ 'match' => '{$M}',
+ 'length' => 4
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 63,
+ 'match' => '{$M: context}',
+ 'length' => 13
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 78,
+ 'match' => '{#M}',
+ 'length' => 4
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 84,
+ 'match' => '{{#M}.regsub()}',
+ 'length' => 15
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 100,
+ 'match' => '',
+ 'length' => 0
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 102,
+ 'match' => '',
+ 'length' => 0
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_UNQUOTED,
+ 'pos' => 103,
+ 'match' => '',
+ 'length' => 0
+ ]
+ ]
+ ],
+ ['/host/key', '#25', 'abc' , '"def"', '1', '1.125', '-1e12', '{$M}', '{$M: context}', '{#M}', '{{#M}.regsub()}', '', '', '']
+ ],
+ [
+ 'nodata(/host/key, "1h")', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'nodata(/host/key, "1h")',
+ 'function' => 'nodata',
+ 'parameters' => [
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUERY,
+ 'pos' => 7,
+ 'match' => '/host/key',
+ 'length' => 9,
+ 'data' => [
+ 'host' => 'host',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]
+ ],
+ [
+ 'type' => CHistFunctionParser::PARAM_TYPE_QUOTED,
+ 'pos' => 18,
+ 'match' => '"1h"',
+ 'length' => 4
+ ]
+ ]
+ ],
+ ['/host/key', '1h']
+ ],
+ [
+ 'last(/{HOST.HOST}/key)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last()', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(10)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last("quoted")', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last({$MACRO})', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/host/key,{$MACRO})', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last("/host/key")', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ],
+ [
+ 'last(/host/key, 1h, abc)', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'function' => '',
+ 'parameters' => []
+ ],
+ []
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $options
+ * @param array $expected
+ * @param array $unquoted_params
+ */
+ public function testParse(string $source, int $pos, array $options, array $expected, array $unquoted_params): void {
+ $hist_function_parser = new CHistFunctionParser($options);
+
+ $this->assertSame($expected, [
+ 'rc' => $hist_function_parser->parse($source, $pos),
+ 'match' => $hist_function_parser->getMatch(),
+ 'function' => $hist_function_parser->getFunction(),
+ 'parameters' => $hist_function_parser->getParameters()
+ ]);
+ $this->assertSame(strlen($expected['match']), $hist_function_parser->getLength());
+ $this->assertSame(count($unquoted_params), count($hist_function_parser->getParameters()));
+
+ foreach ($unquoted_params as $num => $unquoted_param) {
+ $this->assertSame($unquoted_param, $hist_function_parser->getParam($num));
+ }
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CLLDMacroFunctionParserTest.php b/ui/tests/unit/include/classes/parsers/CLLDMacroFunctionParserTest.php
index bbcbd353a7f..279821f9701 100644
--- a/ui/tests/unit/include/classes/parsers/CLLDMacroFunctionParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CLLDMacroFunctionParserTest.php
@@ -88,6 +88,12 @@ class CLLDMacroFunctionParserTest extends CParserTest {
0,
CParser::PARSE_FAIL,
''
+ ],
+ [
+ '{{#M}.somefunc(/host/key["a", "b"])}',
+ 0,
+ CParser::PARSE_FAIL,
+ ''
]
];
}
diff --git a/ui/tests/unit/include/classes/parsers/CPeriodParserTest.php b/ui/tests/unit/include/classes/parsers/CPeriodParserTest.php
new file mode 100644
index 00000000000..7b7528fcebf
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CPeriodParserTest.php
@@ -0,0 +1,245 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CPeriodParserTest extends TestCase {
+
+ /**
+ * An array of relative times and parsed results.
+ */
+ public static function dataProvider() {
+ return [
+ ['', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['', 0, ['lldmacros' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ [':', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['#', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['{#M}', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['{#M}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['{#M}', 0, ['lldmacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{#M}',
+ 'sec_num' => '{#M}',
+ 'time_shift' => ''
+ ]],
+ ['{{#M}.regsub("([a-z]+)", "\1")}', 0, ['lldmacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{#M}.regsub("([a-z]+)", "\1")}',
+ 'sec_num' => '{{#M}.regsub("([a-z]+)", "\1")}',
+ 'time_shift' => ''
+ ]],
+ ['{$M}', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['{$M}', 0, ['lldmacros' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]],
+ ['{$M}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{$M}',
+ 'sec_num' => '{$M}',
+ 'time_shift' => ''
+ ]],
+ ['{$M}:', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{$M}',
+ 'sec_num' => '{$M}',
+ 'time_shift' => ''
+ ]],
+ ['{$M}:{$M: context}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{$M}:{$M: context}',
+ 'sec_num' => '{$M}',
+ 'time_shift' => '{$M: context}'
+ ]],
+ ['{$M}:{#M}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{$M}',
+ 'sec_num' => '{$M}',
+ 'time_shift' => ''
+ ]],
+ ['{$M}:{#M}', 0, ['usermacros' => true, 'lldmacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{$M}:{#M}',
+ 'sec_num' => '{$M}',
+ 'time_shift' => '{#M}'
+ ]],
+ ['#1', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1',
+ 'sec_num' => '#1',
+ 'time_shift' => ''
+ ]],
+ ['1s', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1s',
+ 'sec_num' => '1s',
+ 'time_shift' => ''
+ ]],
+ ['1m', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1m',
+ 'sec_num' => '1m',
+ 'time_shift' => ''
+ ]],
+ ['1h', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1h',
+ 'sec_num' => '1h',
+ 'time_shift' => ''
+ ]],
+ ['1d', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1d',
+ 'sec_num' => '1d',
+ 'time_shift' => ''
+ ]],
+ ['1w', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1w',
+ 'sec_num' => '1w',
+ 'time_shift' => ''
+ ]],
+ ['1M', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1M',
+ 'sec_num' => '1M',
+ 'time_shift' => ''
+ ]],
+ ['1y', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1y',
+ 'sec_num' => '1y',
+ 'time_shift' => ''
+ ]],
+ ['#1:now', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1:now',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now'
+ ]],
+ ['#1:now/y-{$TWO_WEEKS}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1:now/y-{$TWO_WEEKS}',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now/y-{$TWO_WEEKS}'
+ ]],
+ ['#1:now/{$OFFSET}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1:now/{$OFFSET}',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now/{$OFFSET}'
+ ]],
+ ['#1:now/{$OFFSET}-{$TWO_WEEKS}', 0, ['usermacros' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1:now/{$OFFSET}-{$TWO_WEEKS}',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now/{$OFFSET}-{$TWO_WEEKS}'
+ ]],
+ ['#1:now/y-1M', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '#1:now/y-1M',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now/y-1M'
+ ]],
+ ['#1:now/y-1My', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '#1:now/y-1M',
+ 'sec_num' => '#1',
+ 'time_shift' => 'now/y-1M'
+ ]],
+ ['1', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1',
+ 'sec_num' => '1',
+ 'time_shift' => ''
+ ]],
+ ['#1abc', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '#1',
+ 'sec_num' => '#1',
+ 'time_shift' => ''
+ ]],
+ [':now/y', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'sec_num' => '',
+ 'time_shift' => ''
+ ]]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $source
+ * @param int $pos
+ * @param array $options
+ * @param array $expected
+ */
+ public function testParse(string $source, int $pos, array $options, array $expected) {
+ $period_parser = new CPeriodParser($options);
+
+ $this->assertSame($expected, [
+ 'rc' => $period_parser->parse($source, $pos),
+ 'match' => $period_parser->getMatch(),
+ 'sec_num' => $period_parser->getSecNum(),
+ 'time_shift' => $period_parser->getTimeshift()
+ ]);
+ $this->assertSame(strlen($expected['match']), strlen($period_parser->getMatch()));
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CQueryParserTest.php b/ui/tests/unit/include/classes/parsers/CQueryParserTest.php
new file mode 100644
index 00000000000..97a80a5265c
--- /dev/null
+++ b/ui/tests/unit/include/classes/parsers/CQueryParserTest.php
@@ -0,0 +1,427 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CQueryParserTest extends TestCase {
+
+ public function dataProvider() {
+ return [
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/h/i', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/h/i',
+ 'host' => 'h',
+ 'item' => 'i',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /h/i text', 5, [], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/h/i',
+ 'host' => 'h',
+ 'item' => 'i',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST}/item[pam, "param"] text', 5, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST1}/item[pam, "param"] text', 5, ['host_macro' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST}/item[pam, "param"] text', 5, ['host_macro' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/{HOST.HOST}/item[pam, "param"]',
+ 'host' => '{HOST.HOST}',
+ 'item' => 'item[pam, "param"]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST}/item text', 5, ['host_macro_n' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/{HOST.HOST}/item',
+ 'host' => '{HOST.HOST}',
+ 'item' => 'item',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST1}/item text', 5, ['host_macro_n' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/{HOST.HOST1}/item',
+ 'host' => '{HOST.HOST1}',
+ 'item' => 'item',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['text /{HOST.HOST7}/item text', 5, ['host_macro_n' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/{HOST.HOST7}/item',
+ 'host' => '{HOST.HOST7}',
+ 'item' => 'item',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log,ERROR,,1000,,,120.0]', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/Zabbix server/logrt',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server^/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/*', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/*', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/Zabbix server/*',
+ 'host' => 'Zabbix server',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/*/key', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/key', 0, [], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/key', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/*/key', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/*/key',
+ 'host' => '*',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/*/*', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/*/*',
+ 'host' => '*',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/key', 0, ['empty_host' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/'.'/key',
+ 'host' => '',
+ 'item' => 'key',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/*', 0, ['empty_host' => true], [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'host' => '',
+ 'item' => '',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/'.'/*', 0, ['calculated' => true, 'empty_host' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/'.'/*',
+ 'host' => '',
+ 'item' => '*',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[tag = "tag" and group = "group"]', 0, [], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[tag = {$MACRO} and group = "group"]', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[tag = {#MACRO} and group = "group"]', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '',
+ 'tokens' => []
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[{$MACRO} = {{#MACRO}.func()} and group = "group"]', 0, ['usermacros' => true, 'lldmacros' => true, 'calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[{$MACRO} = {{#MACRO}.func()} and group = "group"]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '?[{$MACRO} = {{#MACRO}.func()} and group = "group"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_USER_MACRO,
+ 'pos' => 74,
+ 'match' => '{$MACRO}',
+ 'length' => 8
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 83,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_LLD_MACRO,
+ 'pos' => 85,
+ 'match' => '{{#MACRO}.func()}',
+ 'length' => 17
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 103,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 107,
+ 'match' => 'group',
+ 'length' => 5
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 113,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 115,
+ 'match' => '"group"',
+ 'length' => 7
+ ]
+ ]
+ ]
+ ]],
+ ['/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[tag = "tag" and group = "group"]', 0, ['calculated' => true], [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '/Zabbix server/logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]?[tag = "tag" and group = "group"]',
+ 'host' => 'Zabbix server',
+ 'item' => 'logrt["/home/zabbix32/test[0-9].log",ERROR,,1000,,,120.0]',
+ 'filter' => [
+ 'match' => '?[tag = "tag" and group = "group"]',
+ 'tokens' => [
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 74,
+ 'match' => 'tag',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 78,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 80,
+ 'match' => '"tag"',
+ 'length' => 5
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 86,
+ 'match' => 'and',
+ 'length' => 3
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_KEYWORD,
+ 'pos' => 90,
+ 'match' => 'group',
+ 'length' => 5
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_OPERATOR,
+ 'pos' => 96,
+ 'match' => '=',
+ 'length' => 1
+ ],
+ [
+ 'type' => CFilterParser::TOKEN_TYPE_STRING,
+ 'pos' => 98,
+ 'match' => '"group"',
+ 'length' => 7
+ ]
+ ]
+ ]
+ ]]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ *
+ * @param string $source
+ * @param int $source
+ * @param array $options
+ * @param array $expected
+ */
+ public function testQueryParse(string $source, int $pos, array $options, array $expected) {
+ $query_parser = new CQueryParser($options);
+
+ $this->assertSame($expected, [
+ 'rc' => $query_parser->parse($source, $pos),
+ 'match' => $query_parser->getMatch(),
+ 'host' => $query_parser->getHost(),
+ 'item' => $query_parser->getItem(),
+ 'filter' => $query_parser->getFilter()
+ ]);
+ $this->assertSame(strlen($expected['match']), strlen($query_parser->getMatch()));
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CRelativeTimeParserTest.php b/ui/tests/unit/include/classes/parsers/CRelativeTimeParserTest.php
index 90ede5af3c7..bd8ed45686e 100644
--- a/ui/tests/unit/include/classes/parsers/CRelativeTimeParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CRelativeTimeParserTest.php
@@ -29,7 +29,7 @@ class CRelativeTimeParserTest extends TestCase {
public static function dataProvider() {
return [
[
- 'now', 0,
+ 'now', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [],
@@ -37,7 +37,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/y', 0,
+ 'now/y', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -47,7 +47,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M', 0,
+ 'now/M', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -57,7 +57,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/w', 0,
+ 'now/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -67,7 +67,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/d', 0,
+ 'now/d', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -77,7 +77,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/h', 0,
+ 'now/h', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -87,7 +87,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/m', 0,
+ 'now/m', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -97,7 +97,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/s', 0,
+ 'now/s', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [],
@@ -105,7 +105,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1y', 0,
+ 'now-1y', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -115,7 +115,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1M', 0,
+ 'now-1M', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -125,7 +125,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1w', 0,
+ 'now-1w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -135,7 +135,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1h', 0,
+ 'now-1h', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -145,7 +145,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1m', 0,
+ 'now-1m', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -155,7 +155,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1s', 0,
+ 'now-1s', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -165,7 +165,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-1x', 0,
+ 'now-1x', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [
@@ -175,7 +175,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1y', 0,
+ 'now/M-1y', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -186,7 +186,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1M', 0,
+ 'now/M-1M', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -197,7 +197,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1w', 0,
+ 'now/M-1w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -208,7 +208,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1h', 0,
+ 'now/M-1h', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -219,7 +219,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1m', 0,
+ 'now/M-1m', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -230,7 +230,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1s', 0,
+ 'now/M-1s', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -241,7 +241,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1x', 0,
+ 'now/M-1x', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [
@@ -252,7 +252,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1y/w', 0,
+ 'now/M-1y/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -264,7 +264,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1M/w', 0,
+ 'now/M-1M/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -276,7 +276,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1w/w', 0,
+ 'now/M-1w/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -288,7 +288,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1h/w', 0,
+ 'now/M-1h/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -300,7 +300,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1m/w', 0,
+ 'now/M-1m/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -312,7 +312,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1s/w', 0,
+ 'now/M-1s/w', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -324,7 +324,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-1s/x', 0,
+ 'now/M-1s/x', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [
@@ -335,7 +335,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-300', 0,
+ 'now-300', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -345,7 +345,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-300s', 0,
+ 'now-300s', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -355,7 +355,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-777/d', 0,
+ 'now-777/d', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -366,7 +366,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-300', 0,
+ 'now/M-300', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -377,7 +377,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-300s', 0,
+ 'now/M-300s', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -388,7 +388,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/M-777/d', 0,
+ 'now/M-777/d', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -400,7 +400,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/y/M/w/d/h/m-1y+2M-4w-3d+2h+15m-25s-7/d', 0,
+ 'now/y/M/w/d/h/m-1y+2M-4w-3d+2h+15m-25s-7/d', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
'tokens' => [
@@ -424,7 +424,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now/y/M/w/d/h/m-1y+2?-4w-3d+2h+15m-25s-7/d', 0,
+ 'now/y/M/w/d/h/m-1y+2?-4w-3d+2h+15m-25s-7/d', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [
@@ -441,7 +441,7 @@ class CRelativeTimeParserTest extends TestCase {
]
],
[
- 'now-300sTEXT', 0,
+ 'now-300sTEXT', 0, [],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
'tokens' => [
@@ -449,6 +449,104 @@ class CRelativeTimeParserTest extends TestCase {
],
'match' => 'now-300s'
]
+ ],
+ [
+ '{$M}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'tokens' => [],
+ 'match' => '{$M}'
+ ]
+ ],
+ [
+ '{$M: context}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'tokens' => [],
+ 'match' => '{$M: context}'
+ ]
+ ],
+ [
+ '{$M}TEXT', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'tokens' => [],
+ 'match' => '{$M}'
+ ]
+ ],
+ [
+ '{$M}', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'tokens' => [],
+ 'match' => ''
+ ]
+ ],
+ [
+ '{#M}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'tokens' => [],
+ 'match' => '{#M}'
+ ]
+ ],
+ [
+ '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'tokens' => [],
+ 'match' => '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'
+ ]
+ ],
+ [
+ '{#M}TEXT', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'tokens' => [],
+ 'match' => '{#M}'
+ ]
+ ],
+ [
+ '{#M}', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'tokens' => [],
+ 'match' => ''
+ ]
+ ],
+ [
+ 'now/y/M/w/d/h/m/{#OFFSET}-1y+2-{#ONE_DAY}?-4w-3d+2h+15m-25s-7/d', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'tokens' => [
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'y'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'M'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'w'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'd'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'h'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'm'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_OFFSET, 'sign' => '-', 'value' => '1', 'suffix' => 'y'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_OFFSET, 'sign' => '+', 'value' => '2', 'suffix' => 's']
+ ],
+ 'match' => 'now/y/M/w/d/h/m/{#OFFSET}-1y+2-{#ONE_DAY}'
+ ]
+ ],
+ [
+ 'now/y/M/w/d/h/m/{$OFFSET}-1y+2-{$ONE_DAY}?-4w-3d+2h+15m-25s-7/d', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'tokens' => [
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'y'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'M'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'w'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'd'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'h'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_PRECISION, 'suffix' => 'm'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_OFFSET, 'sign' => '-', 'value' => '1', 'suffix' => 'y'],
+ ['type' => CRelativeTimeParser::ZBX_TOKEN_OFFSET, 'sign' => '+', 'value' => '2', 'suffix' => 's']
+ ],
+ 'match' => 'now/y/M/w/d/h/m/{$OFFSET}-1y+2-{$ONE_DAY}'
+ ]
]
];
}
@@ -458,10 +556,11 @@ class CRelativeTimeParserTest extends TestCase {
*
* @param string $source
* @param int $pos
+ * @param array $options
* @param array $expected
*/
- public function testParse($source, $pos, $expected) {
- $parser = new CRelativeTimeParser();
+ public function testParse($source, $pos, $options, $expected) {
+ $parser = new CRelativeTimeParser($options);
$this->assertSame($expected, [
'rc' => $parser->parse($source, $pos),
diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php
index cd0f4db5e0c..f6f10d6c2e5 100644
--- a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php
+++ b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php
@@ -29,7 +29,7 @@ class CTextTriggerConstructorTest extends TestCase {
protected $constructor;
protected function setUp(): void {
- $this->constructor = new CTextTriggerConstructor(new CTriggerExpression());
+ $this->constructor = new CTextTriggerConstructor(new CExpressionParser(['lldmacros' => true]));
}
public function dataProviderGetExpressionFromPartsValid() {
@@ -43,7 +43,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(({host:item.regexp(test)})<>0)'
+ '((find(/host/item,,"regexp","test"))<>0)'
],
[
'host',
@@ -54,7 +54,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '(({host:item.regexp(test)})=0)'
+ '((find(/host/item,,"regexp","test"))=0)'
],
[
'host',
@@ -65,7 +65,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(({host:item.regexp(a)})<>0 and ({host:item.regexp(b)})<>0)'
+ '((find(/host/item,,"regexp","a"))<>0 and (find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -76,7 +76,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(({host:item.regexp(a)})<>0 or ({host:item.regexp(b)})<>0)'
+ '((find(/host/item,,"regexp","a"))<>0 or (find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -91,7 +91,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((({host:item.regexp(a)})<>0) or (({host:item.regexp(b)})<>0))'
+ '(((find(/host/item,,"regexp","a"))<>0) or ((find(/host/item,,"regexp","b"))<>0))'
],
[
'host',
@@ -106,7 +106,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '(({host:item.regexp(a)})=0) and (({host:item.regexp(b)})=0)'
+ '((find(/host/item,,"regexp","a"))=0) and ((find(/host/item,,"regexp","b"))=0)'
],
[
'host',
@@ -117,11 +117,11 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
],
[
- 'value' => 'regexp(с) or regexp(d)',
+ 'value' => 'regexp(c) or regexp(d)',
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((({host:item.regexp(a)})<>0 and ({host:item.regexp(b)})<>0) or (({host:item.regexp(с)})<>0 or ({host:item.regexp(d)})<>0))'
+ '(((find(/host/item,,"regexp","a"))<>0 and (find(/host/item,,"regexp","b"))<>0) or ((find(/host/item,,"regexp","c"))<>0 or (find(/host/item,,"regexp","d"))<>0))'
],
[
'host',
@@ -136,7 +136,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '(({host:item.regexp(a)})=0 and ({host:item.regexp(b)})=0) and (({host:item.regexp(c)})=0 or ({host:item.regexp(d)})=0)'
+ '((find(/host/item,,"regexp","a"))=0 and (find(/host/item,,"regexp","b"))=0) and ((find(/host/item,,"regexp","c"))=0 or (find(/host/item,,"regexp","d"))=0)'
],
[
@@ -148,7 +148,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(({host:item.iregexp(test)})<>0)'
+ '((find(/host/item,,"iregexp","test"))<>0)'
],
[
'host',
@@ -159,7 +159,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '(({host:item.iregexp(test)})=0)'
+ '((find(/host/item,,"iregexp","test"))=0)'
],
[
'host',
@@ -170,7 +170,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '(({host:item.regexp(a)})=0)'
+ '((find(/host/item,,"regexp","a"))=0)'
],
// "not" cases
@@ -183,7 +183,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '((not {host:item.regexp(test)})=0)'
+ '((not find(/host/item,,"regexp","test"))=0)'
],
[
'host',
@@ -194,7 +194,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '((not {host:item.regexp(test)})=0)'
+ '((not find(/host/item,,"regexp","test"))=0)'
],
[
'host',
@@ -205,7 +205,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((not {host:item.regexp(a)})<>0 and (not {host:item.regexp(b)})<>0)'
+ '((not find(/host/item,,"regexp","a"))<>0 and (not find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -216,7 +216,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((not {host:item.regexp(a)})<>0 or (not {host:item.regexp(b)})<>0)'
+ '((not find(/host/item,,"regexp","a"))<>0 or (not find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -231,7 +231,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(((not {host:item.regexp(a)})<>0) or ((not {host:item.regexp(b)})<>0))'
+ '(((not find(/host/item,,"regexp","a"))<>0) or ((not find(/host/item,,"regexp","b"))<>0))'
],
// "-" cases
@@ -244,7 +244,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '((-{host:item.regexp(test)})=0)'
+ '((-find(/host/item,,"regexp","test"))=0)'
],
[
'host',
@@ -255,7 +255,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH
]
],
- '((-{host:item.regexp(test)})=0)'
+ '((-find(/host/item,,"regexp","test"))=0)'
],
[
'host',
@@ -266,7 +266,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((-{host:item.regexp(a)})<>0 and (-{host:item.regexp(b)})<>0)'
+ '((-find(/host/item,,"regexp","a"))<>0 and (-find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -277,7 +277,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '((-{host:item.regexp(a)})<>0 or (-{host:item.regexp(b)})<>0)'
+ '((-find(/host/item,,"regexp","a"))<>0 or (-find(/host/item,,"regexp","b"))<>0)'
],
[
'host',
@@ -292,7 +292,7 @@ class CTextTriggerConstructorTest extends TestCase {
'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH
]
],
- '(((-{host:item.regexp(a)})<>0) or ((-{host:item.regexp(b)})<>0))'
+ '(((-find(/host/item,,"regexp","a"))<>0) or ((-find(/host/item,,"regexp","b"))<>0))'
]
];
}
@@ -302,28 +302,20 @@ class CTextTriggerConstructorTest extends TestCase {
*
* @dataProvider dataProviderGetExpressionFromPartsValid
*
- * @param $host
- * @param $item
- * @param array $expressions
- * @param $expectedExpressions
+ * @param string $host
+ * @param string $item_key
+ * @param array $expressions
+ * @param string $expected
*/
- public function testGetExpressionFromPartsValid($host, $item, array $expressions, $expectedExpressions) {
- $expression = $this->constructor->getExpressionFromParts($host, $item, $expressions);
-
- $this->assertEquals($expectedExpressions, $expression);
- }
-
- /**
- * Test calling getExpressionFromParts() with invalid parameters.
- */
- public function testGetExpressionFromPartsInvalid() {
- $this->markTestIncomplete();
+ public function testGetExpressionFromPartsValid(string $host, string $item_key, array $expressions,
+ string $expected) {
+ $this->assertSame($expected, $this->constructor->getExpressionFromParts($host, $item_key, $expressions));
}
public function dataProviderGetPartsFromExpression() {
return [
[
- '({Zabbix server:system.hostname.regexp(a)})=0',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))=0',
[
[
'value' => 'regexp(a)',
@@ -332,7 +324,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '({Zabbix server:system.hostname.regexp(a)})<>0',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))<>0',
[
[
'value' => 'regexp(a)',
@@ -341,7 +333,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(({Zabbix server:system.hostname.regexp(a)})=0)',
+ '((find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => 'regexp(a)',
@@ -350,7 +342,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '({Zabbix server:system.hostname.regexp(a)})=0 and ({Zabbix server:system.hostname.regexp(b)})=0',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 and (find(/Zabbix server/system.hostname,,"regexp","b"))=0',
[
[
'value' => 'regexp(a)',
@@ -363,7 +355,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0',
[
[
'value' => 'regexp(a)',
@@ -376,7 +368,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0) and ({Zabbix server:system.hostname.regexp(c)})=0',
+ '((find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0) and (find(/Zabbix server/system.hostname,,"regexp","c"))=0',
[
[
'value' => 'regexp(a) or regexp(b)',
@@ -389,7 +381,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '({Zabbix server:system.hostname.regexp(a)})=0 or (({Zabbix server:system.hostname.regexp(b)})=0 and ({Zabbix server:system.hostname.regexp(c)})=0)',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or ((find(/Zabbix server/system.hostname,,"regexp","b"))=0 and (find(/Zabbix server/system.hostname,,"regexp","c"))=0)',
[
[
'value' => 'regexp(a)',
@@ -402,7 +394,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0 and ({Zabbix server:system.hostname.regexp(c)})=0',
+ '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0 and (find(/Zabbix server/system.hostname,,"regexp","c"))=0',
[
[
'value' => 'regexp(a)',
@@ -419,7 +411,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(({Zabbix server:system.hostname.regexp(a)})=0 and ({Zabbix server:system.hostname.regexp(b)})=0) or (({Zabbix server:system.hostname.regexp(c)})=0 or ({Zabbix server:system.hostname.regexp(d)})=0)',
+ '((find(/Zabbix server/system.hostname,,"regexp","a"))=0 and (find(/Zabbix server/system.hostname,,"regexp","b"))=0) or ((find(/Zabbix server/system.hostname,,"regexp","c"))=0 or (find(/Zabbix server/system.hostname,,"regexp","d"))=0)',
[
[
'value' => 'regexp(a) and regexp(b)',
@@ -432,7 +424,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '((({Zabbix server:system.hostname.regexp(a)})=0) or (({Zabbix server:system.hostname.regexp(b)})=0)) and (({Zabbix server:system.hostname.regexp(c)})=0)',
+ '(((find(/Zabbix server/system.hostname,,"regexp","a"))=0) or ((find(/Zabbix server/system.hostname,,"regexp","b"))=0)) and ((find(/Zabbix server/system.hostname,,"regexp","c"))=0)',
[
[
'value' => 'regexp(a)',
@@ -451,7 +443,7 @@ class CTextTriggerConstructorTest extends TestCase {
// "not" cases
[
- '(not {Zabbix server:system.hostname.regexp(a)})=0',
+ '(not find(/Zabbix server/system.hostname,,"regexp","a"))=0',
[
[
'value' => 'not regexp(a)',
@@ -460,7 +452,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(not ({Zabbix server:system.hostname.regexp(a)})=0)',
+ '(not (find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => 'not regexp(a)',
@@ -469,7 +461,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- 'not (({Zabbix server:system.hostname.regexp(a)})=0)',
+ 'not ((find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => 'not (regexp(a))',
@@ -480,7 +472,7 @@ class CTextTriggerConstructorTest extends TestCase {
// "-" cases
[
- '(-{Zabbix server:system.hostname.regexp(a)})=0',
+ '(-find(/Zabbix server/system.hostname,,"regexp","a"))=0',
[
[
'value' => '-regexp(a)',
@@ -489,7 +481,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(-({Zabbix server:system.hostname.regexp(a)})=0)',
+ '(-(find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => '-regexp(a)',
@@ -498,7 +490,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '-(({Zabbix server:system.hostname.regexp(a)})=0)',
+ '-((find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => '-(regexp(a))',
@@ -507,7 +499,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(- {Zabbix server:system.hostname.regexp(a)})=0',
+ '(- find(/Zabbix server/system.hostname,,"regexp","a"))=0',
[
[
'value' => '-regexp(a)',
@@ -516,7 +508,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '(- ({Zabbix server:system.hostname.regexp(a)})=0)',
+ '(- (find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => '-regexp(a)',
@@ -525,7 +517,7 @@ class CTextTriggerConstructorTest extends TestCase {
]
],
[
- '- (({Zabbix server:system.hostname.regexp(a)})=0)',
+ '- ((find(/Zabbix server/system.hostname,,"regexp","a"))=0)',
[
[
'value' => '-(regexp(a))',
@@ -539,12 +531,10 @@ class CTextTriggerConstructorTest extends TestCase {
/**
* @dataProvider dataProviderGetPartsFromExpression
*
- * @param $expression
- * @param array $expectedParts
+ * @param string $expression
+ * @param array $expected_parts
*/
- public function testGetPartsFromExpression($expression, array $expectedParts) {
- $parts = $this->constructor->getPartsFromExpression($expression);
-
- $this->assertEquals($expectedParts, $parts);
+ public function testGetPartsFromExpression(string $expression, array $expected_parts) {
+ $this->assertSame($expected_parts, $this->constructor->getPartsFromExpression($expression));
}
}
diff --git a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
index 1d72f8eef1e..a4bc6e61301 100644
--- a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
@@ -45,21 +45,75 @@ class CApiInputValidatorTest extends TestCase {
return [
[
['type' => API_CALC_FORMULA],
- 'last(agent.ping) = 1 or "text" = {$MACRO}',
+ 'last(//agent.ping) = 1 or "text" = {$MACRO}',
'/1/formula',
- 'last(agent.ping) = 1 or "text" = {$MACRO}'
+ 'last(//agent.ping) = 1 or "text" = {$MACRO}'
+ ],
+ [
+ ['type' => API_CALC_FORMULA, 'flags' => API_ALLOW_LLD_MACRO],
+ 'last(//agent.ping) = 1 or "text" = {#LLD}',
+ '/1/formula',
+ 'last(//agent.ping) = 1 or "text" = {#LLD}'
],
[
['type' => API_CALC_FORMULA],
- 'last(agent.ping) = 1 or "text" = {#LLD}',
+ '10+sum(/*/counter?[tag="test:1" and group="test-hosts"],1m)',
'/1/formula',
- 'Invalid parameter "/1/formula": incorrect calculated item formula starting from " {#LLD}".'
+ 'Invalid parameter "/1/formula": invalid first parameter in function "sum".'
],
[
- ['type' => API_CALC_FORMULA, 'flags' => API_ALLOW_LLD_MACRO],
- 'last(agent.ping) = 1 or "text" = {#LLD}',
+ ['type' => API_CALC_FORMULA],
+ '10+sum(/host/*?[tag="test:1" and group="test-hosts"],1m)',
+ '/1/formula',
+ 'Invalid parameter "/1/formula": invalid first parameter in function "sum".'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'max(1, max(2, max(3, max(4, max(5, max(6, max(7, max(8, max(9, max(10, max(11, max(12, max(13, max(14, max(15, max(16, max(17, max(18, max(19, max(20, max(21, max(22, max(23, max(24, max(25, max(26, max(27, max(28, max(29, max(30, max(31, max(32, 33))))))))))))))))))))))))))))))))',
+ '/1/formula',
+ 'max(1, max(2, max(3, max(4, max(5, max(6, max(7, max(8, max(9, max(10, max(11, max(12, max(13, max(14, max(15, max(16, max(17, max(18, max(19, max(20, max(21, max(22, max(23, max(24, max(25, max(26, max(27, max(28, max(29, max(30, max(31, max(32, 33))))))))))))))))))))))))))))))))'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'sum(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"]))',
+ '/1/formula',
+ 'sum(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"]))'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'sum(last_foreach(/*/*[/,total]?[group="Any host and item is prohibited"]))',
+ '/1/formula',
+ 'Invalid parameter "/1/formula": incorrect expression starting from "sum(last_foreach(/*/*[/,total]?[group="Any host and item is prohibited"]))".'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'sum(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"])) + last_foreach(/host/key)',
+ '/1/formula',
+ 'Invalid parameter "/1/formula": incorrect usage of function "last_foreach".'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'avg(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"]))',
+ '/1/formula',
+ 'avg(last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"]))'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'last_foreach(/*/vfs.fs.size[/,total]?[group="MySQL Servers"])',
+ '/1/formula',
+ 'Invalid parameter "/1/formula": incorrect usage of function "last_foreach".'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'last(//agent.ping) = 1 or "text" = {#LLD}',
+ '/1/formula',
+ 'Invalid parameter "/1/formula": incorrect expression starting from "{#LLD}".'
+ ],
+ [
+ ['type' => API_CALC_FORMULA],
+ 'max(1, max(2, max(3, max(4, max(5, max(6, max(7, max(8, max(9, max(10, max(11, max(12, max(13, max(14, max(15, max(16, max(17, max(18, max(19, max(20, max(21, max(22, max(23, max(24, max(25, max(26, max(27, max(28, max(29, max(30, max(31, max(32, max(33, 1)))))))))))))))))))))))))))))))))',
'/1/formula',
- 'last(agent.ping) = 1 or "text" = {#LLD}'
+ 'Invalid parameter "/1/formula": incorrect expression starting from "max(1, max(2, max(3, max(4, max(5, max(6, max(7, max(8, max(9, max(10, max(11, max(12, max(13, max(14, max(15, max(16, max(17, max(18, max(19, max(20, max(21, max(22, max(23, max(24, max(25, max(26, max(27, max(28, max(29, max(30, max(31, max(32, max(33, 1)))))))))))))))))))))))))))))))))".'
],
[
['type' => API_CALC_FORMULA],
@@ -3535,45 +3589,45 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_TRIGGER_EXPRESSION, 'length' => 10],
- '{host:item.last()} = 0',
+ 'last(/host/item) = 0',
'/1/expression',
'Invalid parameter "/1/expression": value is too long.'
],
[
['type' => API_TRIGGER_EXPRESSION],
- '{host:item.last() = 0',
+ 'last(/host/item = 0',
'/1/expression',
- 'Invalid parameter "/1/expression": incorrect trigger expression starting from "{host:item.last() = 0".'
+ 'Invalid parameter "/1/expression": incorrect expression starting from "last(/host/item = 0".'
],
[
['type' => API_TRIGGER_EXPRESSION],
'9 and 1',
'/1/expression',
- 'Invalid parameter "/1/expression": trigger expression must contain at least one host:key reference.'
+ 'Invalid parameter "/1/expression": trigger expression must contain at least one /host/key reference.'
],
[
['type' => API_TRIGGER_EXPRESSION],
- '{host:item.last()} = {#LLD_MACRO}',
+ 'last(/host/item) = {#LLD_MACRO}',
'/1/expression',
- 'Invalid parameter "/1/expression": incorrect trigger expression starting from " {#LLD_MACRO}".'
+ 'Invalid parameter "/1/expression": incorrect expression starting from "{#LLD_MACRO}".'
],
[
['type' => API_TRIGGER_EXPRESSION, 'flags' => API_ALLOW_LLD_MACRO],
- '{host:item.last()} = {#LLD_MACRO}',
+ 'last(/host/item) = {#LLD_MACRO}',
'/1/expression',
- '{host:item.last()} = {#LLD_MACRO}'
+ 'last(/host/item) = {#LLD_MACRO}'
],
[
['type' => API_TRIGGER_EXPRESSION],
- '{host:item.last()} = 0',
+ 'last(/host/item) = 0',
'/1/expression',
- '{host:item.last()} = 0'
+ 'last(/host/item) = 0'
],
[
['type' => API_TRIGGER_EXPRESSION],
- '{host:item.last()} = {$USER_MACRO}',
+ 'last(/host/item) = {$USER_MACRO}',
'/1/expression',
- '{host:item.last()} = {$USER_MACRO}'
+ 'last(/host/item) = {$USER_MACRO}'
],
[
['type' => API_TRIGGER_EXPRESSION],
@@ -3609,7 +3663,7 @@ class CApiInputValidatorTest extends TestCase {
['type' => API_EVENT_NAME],
'event name {?{host:item.last() = 0}',
'/1/event_name',
- 'Invalid parameter "/1/event_name": incorrect syntax near "{host:item.last() = 0}".'
+ 'Invalid parameter "/1/event_name": incorrect expression starting from "{host:item.last() = 0}".'
],
[
['type' => API_EVENT_NAME],
@@ -3619,15 +3673,15 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_EVENT_NAME],
- 'event name {?{host:item.last()} = 0}',
+ 'event name {?last(/host/item) = 0}',
'/1/event_name',
- 'event name {?{host:item.last()} = 0}'
+ 'event name {?last(/host/item) = 0}'
],
[
['type' => API_EVENT_NAME],
- 'event name {?{host:item.last()} = {$USER_MACRO}}',
+ 'event name {?last(/host/item) = {$USER_MACRO}}',
'/1/event_name',
- 'event name {?{host:item.last()} = {$USER_MACRO}}'
+ 'event name {?last(/host/item) = {$USER_MACRO}}'
],
[
['type' => API_EVENT_NAME],
diff --git a/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php b/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php
index 9c96af5419e..d3d1148c5b6 100644
--- a/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php
@@ -42,12 +42,13 @@ class CEventNameValidatorTest extends TestCase {
['Incorrect macro except expression macro are ignored {ANY_MACRO_HERE_ {}', true, null],
['Simple expression macro {?100+1} test', true, null],
['Expression macro with modificator {{?100+1-(2)}.anyfunc(2)}', true, null],
- ['Macro as host name {?{{HOST.HOST}:item.func(1)}}', true, null],
- ['Expression macro with incorrect syntax {?123++321}', false, 'incorrect syntax near "+321}"'],
- ['{?Expression macro without closing bracket', false, 'incorrect syntax near "Expression macro without closing bracket"'],
- ['Expression macro without closing bracket at the end of event name {?', false, 'unexpected end of string'],
- ['Nested expression macro not supported {?100+{?20+1}}', false, 'incorrect syntax near "{?20+1}}"'],
- ['Empty expression macro {?}', false, 'incorrect syntax near "}"']
+ ['Macro as host name {?func(/{HOST.HOST}/item)}', true, null],
+ ['Expression macro with incorrect syntax {?123++321}', false, 'incorrect expression starting from "+321}"'],
+ ['Missing closing curly bracket {?123+321', false, 'unexpected end of expression macro'],
+ ['{?Expression macro without closing bracket', false, 'incorrect expression starting from "Expression macro without closing bracket"'],
+ ['Expression macro without closing bracket at the end of event name {?', false, 'incorrect expression starting from ""'],
+ ['Nested expression macro not supported {?100+{?20+1}}', false, 'incorrect expression starting from "{?20+1}}"'],
+ ['Empty expression macro {?}', false, 'incorrect expression starting from "}"']
];
}
diff --git a/ui/tests/unit/include/classes/validators/CExpressionValidatorTest.php b/ui/tests/unit/include/classes/validators/CExpressionValidatorTest.php
new file mode 100644
index 00000000000..959c10bb25d
--- /dev/null
+++ b/ui/tests/unit/include/classes/validators/CExpressionValidatorTest.php
@@ -0,0 +1,131 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CExpressionValidatorTest extends TestCase {
+
+ /**
+ * An array of expressions, options and the expected results.
+ */
+ public function dataProvider() {
+ return [
+ ['avg(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ ['avg(count_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(count_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(count_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(count_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ ['avg(last_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(last_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(last_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(last_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ ['avg(max_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(max_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(max_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(max_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ ['avg(min_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(min_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(min_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(min_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ ['avg(sum_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['max(sum_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['min(sum_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(sum_foreach(/host/key, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ // Unknown function in trigger expression.
+ ['avg_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "avg_foreach"']],
+ ['count_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "count_foreach"']],
+ ['last_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "last_foreach"']],
+ ['max_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "max_foreach"']],
+ ['min_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "min_foreach"']],
+ ['sum_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "sum_foreach"']],
+
+ // Not aggregated.
+ ['avg_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "avg_foreach"']],
+ ['count_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "count_foreach"']],
+ ['last_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "last_foreach"']],
+ ['max_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "max_foreach"']],
+ ['min_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "min_foreach"']],
+ ['sum_foreach(/host/key, 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "sum_foreach"']],
+
+ // Wildcards.
+ ['avg(avg_foreach(/*/key[p1,p2], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/host/*, 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/host/key[*,p2], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/host/key[p1,*], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/host/key[*,*], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/*/key[*,*], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(avg_foreach(/*/*, 1))', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "avg_foreach"']],
+ ['avg(/*/key[p1,p2], 1))', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "avg"']],
+ ['avg(/host/*, 1))', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "avg"']],
+ ['avg(/host/key[*,p2], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(/host/key[p1,*], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg(/host/key[*,*], 1))', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ // Non-aggregating math function.
+ ['length(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "avg_foreach"']],
+
+ // Non-existing math function.
+ ['foo(avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => false, 'error' => 'unknown function "foo"']],
+
+ // More than one parameter for aggregating math function.
+ ['sum(avg_foreach(/host/key, 1), 1)', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "avg_foreach"']],
+ ['sum(1, avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "avg_foreach"']],
+ ['sum(avg_foreach(/host/key, 1), avg_foreach(/host/key, 1))', ['calculated' => true], ['rc' => false, 'error' => 'incorrect usage of function "avg_foreach"']],
+
+ // Host/key reference requirement.
+ ['sum(1, 2, 3)', [], ['rc' => false, 'error' => 'trigger expression must contain at least one /host/key reference']],
+ ['sum(1, 2, 3)', ['calculated' => true], ['rc' => true, 'error' => null]],
+
+ // Incorrect usage of math/history functions.
+ ['foo(/host/item)', [], ['rc' => false, 'error' => 'unknown function "foo"']],
+ ['abs(/host/item)', [], ['rc' => false, 'error' => 'incorrect usage of function "abs"']],
+ ['foo(1, 2, 3)', [], ['rc' => false, 'error' => 'unknown function "foo"']],
+ ['change(1, 2, 3)', [], ['rc' => false, 'error' => 'incorrect usage of function "change"']]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ */
+ public function testExpressionValidator(string $source, array $options, array $expected) {
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => true
+ ] + $options);
+ $expression_validator = new CExpressionValidator($options);
+
+ $expression_parser->parse($source);
+ $tokens = $expression_parser->getResult()->getTokens();
+
+ $this->assertSame($expected, [
+ 'rc' => $expression_validator->validate($tokens),
+ 'error' => $expression_validator->getError()
+ ]);
+ }
+}
diff --git a/ui/tests/unit/include/classes/validators/CFunctionValidatorTest.php b/ui/tests/unit/include/classes/validators/CFunctionValidatorTest.php
deleted file mode 100644
index 0923ab6416a..00000000000
--- a/ui/tests/unit/include/classes/validators/CFunctionValidatorTest.php
+++ /dev/null
@@ -1,876 +0,0 @@
-<?php declare(strict_types=1);
-/*
-** Zabbix
-** Copyright (C) 2001-2021 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-use PHPUnit\Framework\TestCase;
-
-class CFunctionValidatorTest extends TestCase {
-
- private static function parameterSecNum_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1s';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1m';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1h';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1d';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1w';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1K';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1M';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1G';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1T';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- private static function parameterSecNumOffset_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1s';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1m';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1h';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1d';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1w';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1K';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1M';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1G';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1T';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- private static function parameterSec_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1s';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1m';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1h';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1d';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1w';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1K';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1M';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1G';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1T';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- private static function parameterTimeShift_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1s';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1m';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1h';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1d';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1w';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1K';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1M';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1G';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1T';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- private static function parameterPercent_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1s';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1m';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1h';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1d';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1w';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1K';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1M';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1G';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1T';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '-15.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '0.0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1.0123';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1.01234';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.00000';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '1.';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '.1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '.';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '100.0000';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '100.0001';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- private static function parameterString_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '-15';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1.0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#1';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '1{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
- }
- }
-
- return $tests;
- }
-
- private static function parameterOperator_TestCases($func, array $valueTypes, array $params = [], $no = 0) {
- $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT];
-
- $tests = [];
-
- foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) {
- foreach ($valueTypesAny as $valueType) {
- $params[$no] = 'eq';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'ne';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'gt';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'ge';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'lt';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'le';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'like';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'band';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'regexp';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = 'iregexp';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M: /}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{$M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '{#M}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)
- && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true)
- ];
-
- $params[$no] = '';
- $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)];
-
- $params[$no] = '0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#12345';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#01';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#-15';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '#1.0';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = 'gt{$M}';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{$M}gt';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{#M}gt';
- $tests[] = [$func, $params, $valueType, $options, false];
-
- $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}gt';
- $tests[] = [$func, $params, $valueType, $options, false];
- }
- }
-
- return $tests;
- }
-
- /**
- * Tests for trend functions: 'trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum'.
- */
- private static function trendFunctionsTestData() {
- $types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64,
- ITEM_VALUE_TYPE_TEXT
- ];
- $functions = ['trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum'];
- $supported_types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64];
- $test_data = [];
-
- foreach ($functions as $function) {
- foreach ($types as $value_type) {
- $supported_type = ($function === 'trendcount' || in_array($value_type, $supported_types));
-
- $test_data = array_merge($test_data, [
- [$function, ['1M', 'now/M'], $value_type, [], true && $supported_type],
- [$function, ['1M', 'now/M-1w+1d/M'], $value_type, [], true && $supported_type],
- [$function, ['1y', '{$MACRO}'], $value_type, [], true && $supported_type],
- [$function, ['{$MACRO}', 'now/M'], $value_type, [], true && $supported_type],
- [$function, ['{$MACRO}', '{$MACRO}'], $value_type, [], true && $supported_type],
- [$function, ['1y', '{#MACRO}'], $value_type, ['lldmacros' => true], true && $supported_type],
- [$function, ['{#MACRO}', 'now/M'], $value_type, ['lldmacros' => true], true && $supported_type],
- [$function, ['{#MACRO}', '{#MACRO}'], $value_type, ['lldmacros' => true], true && $supported_type],
- [$function, [], $value_type, [], false],
- [$function, ['', ''], $value_type, [], false],
- [$function, ['1y', 'now/M'], $value_type, [], false],
- [$function, ['1M', 'now/w-1w+1d/M'], $value_type, [], false],
- [$function, ['1M', 'now/M-1w+1d/w'], $value_type, [], false],
- [$function, ['${MACRO}1y', '{$MACRO}now/y'], $value_type, [], false]
- ]);
- }
- }
-
- return $test_data;
- }
-
- public static function provider() {
- $valueTypesAny = [
- ITEM_VALUE_TYPE_FLOAT => true,
- ITEM_VALUE_TYPE_STR => true,
- ITEM_VALUE_TYPE_LOG => true,
- ITEM_VALUE_TYPE_UINT64 => true,
- ITEM_VALUE_TYPE_TEXT => true
- ];
- $valueTypesLog = [
- ITEM_VALUE_TYPE_LOG => true
- ];
- $valueTypesNum = [
- ITEM_VALUE_TYPE_FLOAT => true,
- ITEM_VALUE_TYPE_UINT64 => true
- ];
- $valueTypesInt = [
- ITEM_VALUE_TYPE_UINT64 => true
- ];
- $valueTypesStr = [
- ITEM_VALUE_TYPE_STR => true,
- ITEM_VALUE_TYPE_LOG => true,
- ITEM_VALUE_TYPE_TEXT => true
- ];
-
- return array_merge(
- // abschange() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('abschange', $valueTypesAny),
-
- // change() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('change', $valueTypesAny),
-
- // date() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('date', $valueTypesAny),
-
- // dayofmonth() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('dayofmonth', $valueTypesAny),
-
- // dayofweek() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('dayofweek', $valueTypesAny),
-
- // diff() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('diff', $valueTypesAny),
-
- // logseverity() - (ignored) [log]
- self::parameterString_TestCases('logseverity', $valueTypesLog),
-
- // now() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('now', $valueTypesAny),
-
- // prev() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('prev', $valueTypesAny),
-
- // time() - (ignored) [float, int, str, text, log]
- self::parameterString_TestCases('time', $valueTypesAny),
-
- // avg() - (sec or #num, time_shift) [float, int]
- self::parameterSecNum_TestCases('avg', $valueTypesNum),
- self::parameterTimeShift_TestCases('avg', $valueTypesNum, ['#1', ''], 1),
-
- // band() - (sec or #num, mask, time_shift) [int]
- self::parameterSecNumOffset_TestCases('band', $valueTypesInt, ['', '0']),
-// TODO Mask
- self::parameterTimeShift_TestCases('band', $valueTypesInt, ['#1', '0', ''], 2),
-
- // count() - (sec or #num, pattern, operator, time_shift) [float, int, str, text, log]
- self::parameterSecNum_TestCases('count', $valueTypesAny),
-// TODO Pattern
- self::parameterOperator_TestCases('count', $valueTypesAny, ['#1', '', ''], 2),
- self::parameterTimeShift_TestCases('count', $valueTypesAny, ['#1', '', '', ''], 3),
-
- // delta() - (sec or #num, time_shift) [float, int]
- self::parameterSecNum_TestCases('delta', $valueTypesNum),
- self::parameterTimeShift_TestCases('delta', $valueTypesNum, ['#1', ''], 1),
-
- // last() - (sec or #num, time_shift) [float, int, str, text, log]
- self::parameterSecNumOffset_TestCases('last', $valueTypesAny),
- self::parameterTimeShift_TestCases('last', $valueTypesAny, ['#1', ''], 1),
-
- // max() - (sec or #num, time_shift) [float, int]
- self::parameterSecNum_TestCases('max', $valueTypesNum),
- self::parameterTimeShift_TestCases('max', $valueTypesNum, ['#1', ''], 1),
-
- // min() - (sec or #num, time_shift) [float, int]
- self::parameterSecNum_TestCases('min', $valueTypesNum),
- self::parameterTimeShift_TestCases('min', $valueTypesNum, ['#1', ''], 1),
-
- // percentile() - (sec or #num, time_shift, float) [float, int]
- self::parameterSecNum_TestCases('percentile', $valueTypesNum, ['#1', '', '50']),
- self::parameterTimeShift_TestCases('percentile', $valueTypesNum, ['#1', '', '50'], 1),
- self::parameterPercent_TestCases('percentile', $valueTypesNum, ['#1', '', '50'], 2),
-
- // strlen() - (sec or #num, time_shift) [str, text, log]
- self::parameterSecNumOffset_TestCases('strlen', $valueTypesStr),
- self::parameterTimeShift_TestCases('strlen', $valueTypesStr, ['#1', ''], 1),
-
- // sum() - (sec or #num, time_shift) [float, int]
- self::parameterSecNum_TestCases('sum', $valueTypesNum),
- self::parameterTimeShift_TestCases('sum', $valueTypesNum, ['#1', ''], 1),
-
- // fuzzytime() - (sec) [float, int]
- self::parameterTimeShift_TestCases('fuzzytime', $valueTypesNum),
-
- // nodata() - (sec) [float, int, str, text, log]
- self::parameterSec_TestCases('nodata', $valueTypesAny),
-
- // iregexp() - (string, sec or #num) [str, text, log]
- self::parameterString_TestCases('iregexp', $valueTypesStr),
- self::parameterSecNum_TestCases('iregexp', $valueTypesStr, ['', ''], 1),
-
- // logeventid() - (string) [log]
- self::parameterString_TestCases('logeventid', $valueTypesLog),
-
- // logsource() - (string) [log]
- self::parameterString_TestCases('logsource', $valueTypesLog),
-
- // regexp() - (string, sec or #num) [str, text, log]
- self::parameterString_TestCases('regexp', $valueTypesStr),
- self::parameterSecNum_TestCases('regexp', $valueTypesStr, ['', ''], 1),
-
- // str() - (string, sec or #num) [str, text, log]
- self::parameterString_TestCases('str', $valueTypesStr),
- self::parameterSecNum_TestCases('str', $valueTypesStr, ['', ''], 1),
-
- // 'trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum'
- self::trendFunctionsTestData()
- );
- }
-
- /**
- * @dataProvider provider
- */
- public function test_parse($functionName, $functionParamList, $valueType, $options, $expectedResult) {
- $triggerFunctionValidator = new CFunctionValidator($options);
-
- $result = $triggerFunctionValidator->validate([
- 'function' => '',
- 'functionName' => $functionName,
- 'functionParamList' => $functionParamList,
- 'valueType' => $valueType
- ]);
-
- $this->assertSame($result, $expectedResult);
- }
-}
diff --git a/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php b/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php
new file mode 100644
index 00000000000..bc7c61fd059
--- /dev/null
+++ b/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php
@@ -0,0 +1,534 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CHistFunctionValidatorTest extends TestCase {
+
+ /**
+ * An array of history functions, options and the expected results.
+ */
+ public function dataProvider() {
+ return [
+ ['foo(/host/key)', [], ['rc' => false, 'error' => 'unknown function "foo"']],
+ ['avg_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "avg_foreach"']],
+ ['count_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "count_foreach"']],
+ ['last_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "last_foreach"']],
+ ['max_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "max_foreach"']],
+ ['min_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "min_foreach"']],
+ ['sum_foreach(/host/key)', [], ['rc' => false, 'error' => 'unknown function "sum_foreach"']],
+
+ ['avg(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "avg"']],
+ ['avg(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, #2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 35791394m)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 596523h)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 24855d)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 3550w)', [], ['rc' => true, 'error' => null]],
+ ['avg(/host/key, 2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 35791395m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 596524h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 24856d)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+ ['avg(/host/key, 3551w)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
+
+ ['avg(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "avg"']],
+
+ ['min(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "min"']],
+ ['min(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "min"']],
+ ['min(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "min"']],
+ ['min(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "min"']],
+ ['min(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "min"']],
+ ['min(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['min(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "min"']],
+
+ ['max(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "max"']],
+ ['max(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "max"']],
+ ['max(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "max"']],
+ ['max(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "max"']],
+ ['max(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "max"']],
+ ['max(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['max(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "max"']],
+
+ ['sum(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "sum"']],
+ ['sum(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sum"']],
+ ['sum(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sum"']],
+ ['sum(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sum"']],
+ ['sum(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sum"']],
+ ['sum(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['sum(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "sum"']],
+
+ ['change(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['change(/host/key,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "change"']],
+
+ ['last(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key,)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key,1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key,1s)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, 1m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, 1m:now/h-1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['last(/host/key, #2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, 2147483647)', [], ['rc' => false, 'error' => 'invalid second parameter in function "last"']],
+ ['last(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "last"']],
+
+ ['logseverity(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key,)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key,1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key,1s)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, 1m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, 1m:now/h-1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['logseverity(/host/key, #2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, 2147483647)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logseverity"']],
+ ['logseverity(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "logseverity"']],
+
+ ['logeventid(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key,)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key,1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key,1s)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, 1m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, 1m:now/h-1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, 2147483647)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logeventid"']],
+ ['logeventid(/host/key, #256,)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, "str")', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, 10)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, 10K)', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['logeventid(/host/key, #256, "str",)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "logeventid"']],
+
+ ['logsource(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key,)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key,1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key,1s)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, 1m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, 1m:now/h-1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, 2147483647)', [], ['rc' => false, 'error' => 'invalid second parameter in function "logsource"']],
+ ['logsource(/host/key, #256,)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, "str")', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, 10)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, 10K)', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['logsource(/host/key, #256, "str",)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "logsource"']],
+
+ ['count(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "count"']],
+ ['count(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "count"']],
+ ['count(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "count"']],
+ ['count(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "count"']],
+ ['count(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "count"']],
+ ['count(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256,)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "str")', [], ['rc' => false, 'error' => 'invalid third parameter in function "count"']],
+ ['count(/host/key, #256, 10)', [], ['rc' => false, 'error' => 'invalid third parameter in function "count"']],
+ ['count(/host/key, #256, 10K)', [], ['rc' => false, 'error' => 'invalid third parameter in function "count"']],
+ ['count(/host/key, #256, "eq")', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "ne")', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "like")', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "bitand")', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp",)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp", 100)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp", 1s)', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp", {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp", {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['count(/host/key, #256, "regexp",,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "count"']],
+
+ ['find(/host/key)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key,)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "find"']],
+ ['find(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "find"']],
+ ['find(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "find"']],
+ ['find(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256,)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "str")', [], ['rc' => false, 'error' => 'invalid third parameter in function "find"']],
+ ['find(/host/key, #256, 10)', [], ['rc' => false, 'error' => 'invalid third parameter in function "find"']],
+ ['find(/host/key, #256, 10K)', [], ['rc' => false, 'error' => 'invalid third parameter in function "find"']],
+ ['find(/host/key, #256, "eq")', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "ne")', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "like")', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "bitand")', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp",)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp", 100)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp", 1s)', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp", {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp", {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['find(/host/key, #256, "regexp",,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "find"']],
+
+ ['forecast(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "forecast"']],
+ ['forecast(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
+ ['forecast(/host/key,,10h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
+ ['forecast(/host/key,#1)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "forecast"']],
+ ['forecast(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
+ ['forecast(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
+ ['forecast(/host/key,1,5s)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key,#1,10h)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key,1s,-7d)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key,1s, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key,1s, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key,1s, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, 1m,)', [], ['rc' => false, 'error' => 'invalid third parameter in function "forecast"']],
+ ['forecast(/host/key, 1M,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
+ ['forecast(/host/key, 1m:now/h-1h, 1d)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #3:now/h-1h, 1m)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #5:now/M, 1w)', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "linear")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "polynomial3")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "{$M}")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, {$M})', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "foo")', [], ['rc' => false, 'error' => 'invalid fourth parameter in function "forecast"']],
+ ['forecast(/host/key, #256, 30d, 25d)', [], ['rc' => false, 'error' => 'invalid fourth parameter in function "forecast"']],
+ ['forecast(/host/key, #256, 30d, "exponential", "delta")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "exponential", "avg")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "exponential", "bar")', [], ['rc' => false, 'error' => 'invalid fifth parameter in function "forecast"']],
+ ['forecast(/host/key, #256, 30d, "exponential", "{#M}")', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "exponential", {#M})', [], ['rc' => true, 'error' => null]],
+ ['forecast(/host/key, #256, 30d, "exponential", "min",)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "forecast"']],
+
+ ['timeleft(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "timeleft"']],
+ ['timeleft(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "timeleft"']],
+ ['timeleft(/host/key,#1)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "timeleft"']],
+ ['timeleft(/host/key,#1,)', [], ['rc' => false, 'error' => 'invalid third parameter in function "timeleft"']],
+ ['timeleft(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "timeleft"']],
+ ['timeleft(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "timeleft"']],
+ ['timeleft(/host/key,1,5s)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key,#1,10h)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key,1s,-7d)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key,1s, "abc")', [], ['rc' => false, 'error' => 'invalid third parameter in function "timeleft"']],
+ ['timeleft(/host/key,1s, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key,1s, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key,1s, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, 1m:now/h-1h, 1d)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #3:now/h-1h, 1m)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #5:now/M, 1w)', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #256, 30d, "linear")', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #256, 30d, "polynomial3")', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #256, 30d, "{$M}")', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #256, 30d, {$M})', [], ['rc' => true, 'error' => null]],
+ ['timeleft(/host/key, #256, 30d, "foo")', [], ['rc' => false, 'error' => 'invalid fourth parameter in function "timeleft"']],
+ ['timeleft(/host/key, #256, 30d, 25d)', [], ['rc' => false, 'error' => 'invalid fourth parameter in function "timeleft"']],
+ ['timeleft(/host/key, #256, 30d, "exponential",)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "timeleft"']],
+
+ ['percentile(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "percentile"']],
+ ['percentile(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "percentile"']],
+ ['percentile(/host/key, #1)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "percentile"']],
+ ['percentile(/host/key, #1,)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "percentile"']],
+ ['percentile(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "percentile"']],
+ ['percentile(/host/key, 1h:now/h-1h, "abc")', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1h:now/h-1h, 5s)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1h:now/h-1h, 1m)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1h:now/h-1h, -1)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1h:now/h-1h, 101)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1h, 0.12345)', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1, 0)', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, 1h, 0.1234)', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, 1h, 99.9999)', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, #1, 100)', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, 1s, "abc")', [], ['rc' => false, 'error' => 'invalid third parameter in function "percentile"']],
+ ['percentile(/host/key, 1s, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, 1s, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, 1s, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['percentile(/host/key, #256, 5,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "percentile"']],
+
+ ['fuzzytime(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "fuzzytime"']],
+ ['fuzzytime(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 1)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 1h)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, "{$MACRO}{#LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, "1h")', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 35791394m)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 596523h)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 24855d)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 3550w)', [], ['rc' => true, 'error' => null]],
+ ['fuzzytime(/host/key, 2147483648)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 35791395m)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 596524h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 24856d)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 3551w)', [], ['rc' => false, 'error' => 'invalid second parameter in function "fuzzytime"']],
+ ['fuzzytime(/host/key, 1h,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "fuzzytime"']],
+
+ ['nodata(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "nodata"']],
+ ['nodata(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "nodata"']],
+ ['nodata(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "nodata"']],
+ ['nodata(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "nodata"']],
+ ['nodata(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "nodata"']],
+ ['nodata(/host/key, 1)', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h)', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, "{$MACRO}{#LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, "1h")', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h,)', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h,)', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h, "strict")', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h, "{$MACRO}{#LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['nodata(/host/key, 1h, "foor")', [], ['rc' => false, 'error' => 'invalid third parameter in function "nodata"']],
+ ['nodata(/host/key, 1h, "strict",)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "nodata"']],
+
+ ['trendavg(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "trendavg"']],
+ ['trendavg(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, 1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, 1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendavg"']],
+ ['trendavg(/host/key, 1h:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, 1d:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, 1y:now/M-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, {$PERIOD}:{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, {$PERIOD}:now-{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendavg(/host/key, 1y:now/y,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "trendavg"']],
+
+ ['trendcount(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "trendcount"']],
+ ['trendcount(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, 1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, 1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendcount"']],
+ ['trendcount(/host/key, 1h:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, 1d:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, 1y:now/M-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, {$PERIOD}:{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, {$PERIOD}:now-{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendcount(/host/key, 1y:now/y,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "trendcount"']],
+
+ ['trendmax(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "trendmax"']],
+ ['trendmax(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, 1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, 1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmax"']],
+ ['trendmax(/host/key, 1h:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, 1d:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, 1y:now/M-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, {$PERIOD}:{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, {$PERIOD}:now-{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendmax(/host/key, 1y:now/y,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "trendmax"']],
+
+ ['trendmin(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "trendmin"']],
+ ['trendmin(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, 1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, 1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendmin"']],
+ ['trendmin(/host/key, 1h:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, 1d:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, 1y:now/M-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, {$PERIOD}:{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, {$PERIOD}:now-{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendmin(/host/key, 1y:now/y,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "trendmin"']],
+
+ ['trendsum(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "trendsum"']],
+ ['trendsum(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, 0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, #0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, #1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, 1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, 1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "trendsum"']],
+ ['trendsum(/host/key, 1h:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, 1d:now-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, 1y:now/M-1h)', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, {$PERIOD}:{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, {$PERIOD}:now-{$TIMESHIFT})', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['trendsum(/host/key, 1y:now/y,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "trendsum"']],
+
+ ['avg_foreach(/host/key)', ['calculated' => true], ['rc' => false, 'error' => 'mandatory parameter is missing in function "avg_foreach"']],
+ ['avg_foreach(/host/key,)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, 0)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, #0)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, #1)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, 1)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg_foreach(/host/key, 1h)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg_foreach(/host/key, 1d)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg_foreach(/host/key, 1d:now/d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, {$PERIOD}:{$TIMESHIFT})', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, {$PERIOD}:now-{$TIMESHIFT})', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "avg_foreach"']],
+ ['avg_foreach(/host/key, {$MACRO})', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg_foreach(/host/key, {#LLDMACRO})', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['avg_foreach(/host/key, 1d,)', ['calculated' => true], ['rc' => false, 'error' => 'invalid number of parameters in function "avg_foreach"']],
+
+ ['last_foreach(/host/key)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key,)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, 0)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, #0)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, #1)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, 1)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, 1h)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, 1d)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, 1d:now/d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, {$PERIOD}:{$TIMESHIFT})', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, {$PERIOD}:now-1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, {$PERIOD}:now-{$TIMESHIFT})', ['calculated' => true], ['rc' => false, 'error' => 'invalid second parameter in function "last_foreach"']],
+ ['last_foreach(/host/key, {$MACRO})', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, {#LLDMACRO})', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['last_foreach(/host/key, 1d,)', ['calculated' => true], ['rc' => false, 'error' => 'invalid number of parameters in function "last_foreach"']],
+
+ // Wildcards in non-aggregating functions.
+ ['sum(/*/key[p1,p2], 1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum"']],
+ ['sum(/host/*, 1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum"']],
+ ['sum(/host/key[*,p2], 1d)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(/host/key[p1,*], 1d)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(/host/key[*,*], 1d)', ['calculated' => true], ['rc' => true, 'error' => null]],
+ ['sum(/*/*, 1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum"']],
+ ['sum(/*/key[*,p2], 1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum"']],
+
+ // Wildcards in aggregating functions.
+ ['sum_foreach(/*/key[p1,p2], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+ ['sum_foreach(/host/*, 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+ ['sum_foreach(/host/key[*,p2], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+ ['sum_foreach(/host/key[p1,*], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+ ['sum_foreach(/host/key[*,*], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+ ['sum_foreach(/*/*, 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum_foreach"']],
+ ['sum_foreach(/*/key[*,p2], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]],
+
+ // Filters.
+ ['sum(/host/key?[tag="foo"], 1d)', ['calculated' => true], ['rc' => false, 'error' => 'invalid first parameter in function "sum"']],
+ ['sum_foreach(/host/key?[tag="foo"], 1d)', ['calculated' => true, 'aggregating' => true], ['rc' => true, 'error' => null]]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProvider
+ */
+ public function testHistFunctionValidator(string $source, array $options, array $expected) {
+ $expression_parser = new CExpressionParser([
+ 'lldmacros' => true
+ ] + $options);
+ $hist_function_validator = new CHistFunctionValidator([
+ 'parameters' => (new CHistFunctionData($options))->getParameters()
+ ] + $options);
+ $expression_parser->parse($source);
+ $tokens = $expression_parser->getResult()->getTokens();
+
+ $this->assertSame(CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION, $tokens[0]['type']);
+ $this->assertSame($expected, [
+ 'rc' => $hist_function_validator->validate($tokens[0]),
+ 'error' => $hist_function_validator->getError()
+ ]);
+ }
+}
diff --git a/ui/tests/unit/include/triggerExpressionReplaceHostTest.php b/ui/tests/unit/include/triggerExpressionReplaceHostTest.php
index fae85f61ed0..237b9777bf0 100644
--- a/ui/tests/unit/include/triggerExpressionReplaceHostTest.php
+++ b/ui/tests/unit/include/triggerExpressionReplaceHostTest.php
@@ -29,39 +29,40 @@ class triggerExpressionReplaceHostTest extends TestCase {
public static function dataProvider() {
return [
[
- '{host:item.func()}',
+ 'func(/host/item)',
'host', 'Zabbix server',
- '{Zabbix server:item.func()}'
+ 'func(/Zabbix server/item)'
],
[
- '5 + {host:item.func()} <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'.
- ' {host:item.func()} or {host2:item2.func()}',
- 'host',
- 'Zabbix server',
- '5 + {Zabbix server:item.func()} <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'.
- ' {Zabbix server:item.func()} or {host2:item2.func()}'
+ '5 + func(/host/item) <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'.
+ ' func(/host/item) or func(/host2/item2)',
+ 'host', 'Zabbix server',
+ '5 + func(/Zabbix server/item) <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'.
+ ' func(/Zabbix server/item) or func(/host2/item2)'
],
[
- '5 + {host:item.func()} <> 0 or {$MACRO: "context"} or'.
+ '5 + func(/host/item) <> 0 or {$MACRO: "context"} or'.
' {{#MACRO}.regsub("^([0-9]+)", "{#MACRO}: \1")} or {TRIGGER.VALUE} or'.
- ' {host:item.func()} or {host2:item2.func()}',
- 'host',
- 'Zabbix server',
- '5 + {Zabbix server:item.func()} <> 0 or {$MACRO: "context"} or'.
+ ' func(/host/item) or func(/host2/item2)',
+ 'host', 'Zabbix server',
+ '5 + func(/Zabbix server/item) <> 0 or {$MACRO: "context"} or'.
' {{#MACRO}.regsub("^([0-9]+)", "{#MACRO}: \1")} or {TRIGGER.VALUE} or'.
- ' {Zabbix server:item.func()} or {host2:item2.func()}'
+ ' func(/Zabbix server/item) or func(/host2/item2)'
+ ],
+ [
+ 'func(/host/item) or {{#M}.regsub("func(/host/item)", "\1")}',
+ 'host', 'Zabbix server',
+ 'func(/Zabbix server/item) or {{#M}.regsub("func(/host/item)", "\1")}'
],
[
- '{host:item.func()} or {{#M}.regsub("{host:item.func()}", "\1")}',
- 'host',
- 'Zabbix server',
- '{Zabbix server:item.func()} or {{#M}.regsub("{host:item.func()}", "\1")}'
+ '5 + func(/Zabbix server/item) <> 0 or func(/Zabbix server/item) or func(/host2/item2)',
+ 'Zabbix server', 'host',
+ '5 + func(/host/item) <> 0 or func(/host/item) or func(/host2/item2)'
],
[
- '5 + {Zabbix server:item.func()} <> 0 or {Zabbix server:item.func()} or {host2:item2.func()}',
- 'Zabbix server',
- 'host',
- '5 + {host:item.func()} <> 0 or {host:item.func()} or {host2:item2.func()}'
+ 'min(func(/host/item), func(/host/item), "func(/host/item)") = "func(/host/item)"',
+ 'host', 'Zabbix server',
+ 'min(func(/Zabbix server/item), func(/Zabbix server/item), "func(/host/item)") = "func(/host/item)"'
]
];
}
diff --git a/ui/tests/unit/include/triggers/GetExpressionTreeTest.php b/ui/tests/unit/include/triggers/GetExpressionTreeTest.php
index d38179e85ab..720f2b7d630 100644
--- a/ui/tests/unit/include/triggers/GetExpressionTreeTest.php
+++ b/ui/tests/unit/include/triggers/GetExpressionTreeTest.php
@@ -22,14 +22,14 @@
use PHPUnit\Framework\TestCase;
class GetExpressionTreeTest extends TestCase {
- private $trigger_expression;
+ private $expression_parser;
protected function setUp(): void {
- $this->trigger_expression = new CTriggerExpression();
+ $this->expression_parser = new CExpressionParser();
}
protected function tearDown(): void {
- $this->trigger_expression = null;
+ $this->expression_parser = null;
}
public function provider() {
@@ -102,11 +102,9 @@ class GetExpressionTreeTest extends TestCase {
* @param $expected_parsed
*/
public function test($expression, $expected_parsed) {
- if (!$this->trigger_expression->parse($expression)) {
- $this->fail('CTriggerExpression parse error');
- }
+ $this->assertSame(CParser::PARSE_SUCCESS, $this->expression_parser->parse($expression));
- $result = getExpressionTree($this->trigger_expression, 0, strlen($this->trigger_expression->expression) - 1);
+ $result = getExpressionTree($this->expression_parser, 0, $this->expression_parser->getLength() - 1);
if (!is_array($result)) {
$this->fail('getExpressionTree did not return an array');