1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22/**
23 * Class containing methods for operations with items.
24 */
25class CItem extends CItemGeneral {
26
27	protected $tableName = 'items';
28	protected $tableAlias = 'i';
29	protected $sortColumns = ['itemid', 'name', 'key_', 'delay', 'history', 'trends', 'type', 'status'];
30
31	/**
32	 * Define a set of supported pre-processing rules.
33	 *
34	 * @var array
35	 */
36	const SUPPORTED_PREPROCESSING_TYPES = [ZBX_PREPROC_REGSUB, ZBX_PREPROC_TRIM, ZBX_PREPROC_RTRIM,
37		ZBX_PREPROC_LTRIM, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_DELTA_VALUE,
38		ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
39		ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
40		ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
41		ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
42		ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
43		ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
44	];
45
46	/**
47	 * Define a set of supported item types.
48	 *
49	 * @var array
50	 */
51	const SUPPORTED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
52		ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
53		ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT,
54		ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
55	];
56
57	public function __construct() {
58		parent::__construct();
59
60		$this->errorMessages = array_merge($this->errorMessages, [
61			self::ERROR_EXISTS_TEMPLATE => _('Item "%1$s" already exists on "%2$s", inherited from another template.'),
62			self::ERROR_EXISTS => _('Item "%1$s" already exists on "%2$s".'),
63			self::ERROR_INVALID_KEY => _('Invalid key "%1$s" for item "%2$s" on "%3$s": %4$s.')
64		]);
65	}
66
67	/**
68	 * Get items data.
69	 *
70	 * @param array  $options
71	 * @param array  $options['itemids']
72	 * @param array  $options['hostids']
73	 * @param array  $options['groupids']
74	 * @param array  $options['triggerids']
75	 * @param bool   $options['status']
76	 * @param bool   $options['templated_items']
77	 * @param bool   $options['editable']
78	 * @param bool   $options['count']
79	 * @param string $options['pattern']
80	 * @param int    $options['limit']
81	 * @param string $options['order']
82	 *
83	 * @return array|int item data as array or false if error
84	 */
85	public function get($options = []) {
86		$result = [];
87
88		$sqlParts = [
89			'select'	=> ['items' => 'i.itemid'],
90			'from'		=> ['items' => 'items i'],
91			'where'		=> ['webtype' => 'i.type<>'.ITEM_TYPE_HTTPTEST, 'flags' => 'i.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'],
92			'group'		=> [],
93			'order'		=> [],
94			'limit'		=> null
95		];
96
97		$defOptions = [
98			'groupids'					=> null,
99			'templateids'				=> null,
100			'hostids'					=> null,
101			'proxyids'					=> null,
102			'itemids'					=> null,
103			'interfaceids'				=> null,
104			'graphids'					=> null,
105			'triggerids'				=> null,
106			'webitems'					=> null,
107			'inherited'					=> null,
108			'templated'					=> null,
109			'monitored'					=> null,
110			'editable'					=> false,
111			'nopermissions'				=> null,
112			'group'						=> null,
113			'host'						=> null,
114			'with_triggers'				=> null,
115			'evaltype'					=> TAG_EVAL_TYPE_AND_OR,
116			'tags'						=> null,
117			// filter
118			'filter'					=> null,
119			'search'					=> null,
120			'searchByAny'				=> null,
121			'startSearch'				=> false,
122			'excludeSearch'				=> false,
123			'searchWildcardsEnabled'	=> null,
124			// output
125			'output'					=> API_OUTPUT_EXTEND,
126			'selectHosts'				=> null,
127			'selectInterfaces'			=> null,
128			'selectTags'				=> null,
129			'selectTriggers'			=> null,
130			'selectGraphs'				=> null,
131			'selectDiscoveryRule'		=> null,
132			'selectItemDiscovery'		=> null,
133			'selectPreprocessing'		=> null,
134			'selectValueMap'			=> null,
135			'countOutput'				=> false,
136			'groupCount'				=> false,
137			'preservekeys'				=> false,
138			'sortfield'					=> '',
139			'sortorder'					=> '',
140			'limit'						=> null,
141			'limitSelects'				=> null
142		];
143		$options = zbx_array_merge($defOptions, $options);
144		$this->validateGet($options);
145
146		// editable + permission check
147		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) {
148			$permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ;
149			$userGroups = getUserGroupsByUserId(self::$userData['userid']);
150
151			$sqlParts['where'][] = 'EXISTS ('.
152					'SELECT NULL'.
153					' FROM hosts_groups hgg'.
154						' JOIN rights r'.
155							' ON r.id=hgg.groupid'.
156								' AND '.dbConditionInt('r.groupid', $userGroups).
157					' WHERE i.hostid=hgg.hostid'.
158					' GROUP BY hgg.hostid'.
159					' HAVING MIN(r.permission)>'.PERM_DENY.
160						' AND MAX(r.permission)>='.zbx_dbstr($permission).
161					')';
162		}
163
164		// itemids
165		if (!is_null($options['itemids'])) {
166			zbx_value2array($options['itemids']);
167
168			$sqlParts['where']['itemid'] = dbConditionInt('i.itemid', $options['itemids']);
169		}
170
171		// templateids
172		if (!is_null($options['templateids'])) {
173			zbx_value2array($options['templateids']);
174
175			if (!is_null($options['hostids'])) {
176				zbx_value2array($options['hostids']);
177				$options['hostids'] = array_merge($options['hostids'], $options['templateids']);
178			}
179			else {
180				$options['hostids'] = $options['templateids'];
181			}
182		}
183
184		// hostids
185		if (!is_null($options['hostids'])) {
186			zbx_value2array($options['hostids']);
187
188			$sqlParts['where']['hostid'] = dbConditionInt('i.hostid', $options['hostids']);
189
190			if ($options['groupCount']) {
191				$sqlParts['group']['i'] = 'i.hostid';
192			}
193		}
194
195		// interfaceids
196		if (!is_null($options['interfaceids'])) {
197			zbx_value2array($options['interfaceids']);
198
199			$sqlParts['where']['interfaceid'] = dbConditionId('i.interfaceid', $options['interfaceids']);
200
201			if ($options['groupCount']) {
202				$sqlParts['group']['i'] = 'i.interfaceid';
203			}
204		}
205
206		// groupids
207		if (!is_null($options['groupids'])) {
208			zbx_value2array($options['groupids']);
209
210			$sqlParts['from']['hosts_groups'] = 'hosts_groups hg';
211			$sqlParts['where'][] = dbConditionInt('hg.groupid', $options['groupids']);
212			$sqlParts['where'][] = 'hg.hostid=i.hostid';
213
214			if ($options['groupCount']) {
215				$sqlParts['group']['hg'] = 'hg.groupid';
216			}
217		}
218
219		// proxyids
220		if (!is_null($options['proxyids'])) {
221			zbx_value2array($options['proxyids']);
222
223			$sqlParts['from']['hosts'] = 'hosts h';
224			$sqlParts['where'][] = dbConditionId('h.proxy_hostid', $options['proxyids']);
225			$sqlParts['where'][] = 'h.hostid=i.hostid';
226
227			if ($options['groupCount']) {
228				$sqlParts['group']['h'] = 'h.proxy_hostid';
229			}
230		}
231
232		// triggerids
233		if (!is_null($options['triggerids'])) {
234			zbx_value2array($options['triggerids']);
235
236			$sqlParts['from']['functions'] = 'functions f';
237			$sqlParts['where'][] = dbConditionInt('f.triggerid', $options['triggerids']);
238			$sqlParts['where']['if'] = 'i.itemid=f.itemid';
239		}
240
241		// tags
242		if ($options['tags'] !== null && $options['tags']) {
243			$sqlParts['where'][] = CApiTagHelper::addWhereCondition($options['tags'], $options['evaltype'], 'i',
244				'item_tag', 'itemid'
245			);
246		}
247
248		// graphids
249		if (!is_null($options['graphids'])) {
250			zbx_value2array($options['graphids']);
251
252			$sqlParts['from']['graphs_items'] = 'graphs_items gi';
253			$sqlParts['where'][] = dbConditionInt('gi.graphid', $options['graphids']);
254			$sqlParts['where']['igi'] = 'i.itemid=gi.itemid';
255		}
256
257		// webitems
258		if (!is_null($options['webitems'])) {
259			unset($sqlParts['where']['webtype']);
260		}
261
262		// inherited
263		if (!is_null($options['inherited'])) {
264			if ($options['inherited']) {
265				$sqlParts['where'][] = 'i.templateid IS NOT NULL';
266			}
267			else {
268				$sqlParts['where'][] = 'i.templateid IS NULL';
269			}
270		}
271
272		// templated
273		if (!is_null($options['templated'])) {
274			$sqlParts['from']['hosts'] = 'hosts h';
275			$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
276
277			if ($options['templated']) {
278				$sqlParts['where'][] = 'h.status='.HOST_STATUS_TEMPLATE;
279			}
280			else {
281				$sqlParts['where'][] = 'h.status<>'.HOST_STATUS_TEMPLATE;
282			}
283		}
284
285		// monitored
286		if (!is_null($options['monitored'])) {
287			$sqlParts['from']['hosts'] = 'hosts h';
288			$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
289
290			if ($options['monitored']) {
291				$sqlParts['where'][] = 'h.status='.HOST_STATUS_MONITORED;
292				$sqlParts['where'][] = 'i.status='.ITEM_STATUS_ACTIVE;
293			}
294			else {
295				$sqlParts['where'][] = '(h.status<>'.HOST_STATUS_MONITORED.' OR i.status<>'.ITEM_STATUS_ACTIVE.')';
296			}
297		}
298
299		// search
300		if (is_array($options['search'])) {
301			if (array_key_exists('error', $options['search']) && $options['search']['error'] !== null) {
302				zbx_db_search('item_rtdata ir', ['search' => ['error' => $options['search']['error']]] + $options,
303					$sqlParts
304				);
305			}
306
307			zbx_db_search('items i', $options, $sqlParts);
308		}
309
310		// filter
311		if (is_array($options['filter'])) {
312			if (array_key_exists('delay', $options['filter']) && $options['filter']['delay'] !== null) {
313				$sqlParts['where'][] = makeUpdateIntervalFilter('i.delay', $options['filter']['delay']);
314				unset($options['filter']['delay']);
315			}
316
317			if (array_key_exists('history', $options['filter']) && $options['filter']['history'] !== null) {
318				$options['filter']['history'] = getTimeUnitFilters($options['filter']['history']);
319			}
320
321			if (array_key_exists('trends', $options['filter']) && $options['filter']['trends'] !== null) {
322				$options['filter']['trends'] = getTimeUnitFilters($options['filter']['trends']);
323			}
324
325			if (array_key_exists('state', $options['filter']) && $options['filter']['state'] !== null) {
326				$this->dbFilter('item_rtdata ir', ['filter' => ['state' => $options['filter']['state']]] + $options,
327					$sqlParts
328				);
329			}
330
331			$this->dbFilter('items i', $options, $sqlParts);
332
333			if (isset($options['filter']['host'])) {
334				zbx_value2array($options['filter']['host']);
335
336				$sqlParts['from']['hosts'] = 'hosts h';
337				$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
338				$sqlParts['where']['h'] = dbConditionString('h.host', $options['filter']['host'], false, true);
339			}
340
341			if (array_key_exists('flags', $options['filter'])
342					&& (is_null($options['filter']['flags']) || !zbx_empty($options['filter']['flags']))) {
343				unset($sqlParts['where']['flags']);
344			}
345		}
346
347		// group
348		if (!is_null($options['group'])) {
349			$sqlParts['from']['hstgrp'] = 'hstgrp g';
350			$sqlParts['from']['hosts_groups'] = 'hosts_groups hg';
351			$sqlParts['where']['ghg'] = 'g.groupid=hg.groupid';
352			$sqlParts['where']['hgi'] = 'hg.hostid=i.hostid';
353			$sqlParts['where'][] = ' g.name='.zbx_dbstr($options['group']);
354		}
355
356		// host
357		if (!is_null($options['host'])) {
358			$sqlParts['from']['hosts'] = 'hosts h';
359			$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
360			$sqlParts['where'][] = ' h.host='.zbx_dbstr($options['host']);
361		}
362
363		// with_triggers
364		if (!is_null($options['with_triggers'])) {
365			if ($options['with_triggers'] == 1) {
366				$sqlParts['where'][] = 'EXISTS ('.
367					'SELECT NULL'.
368					' FROM functions ff,triggers t'.
369					' WHERE i.itemid=ff.itemid'.
370						' AND ff.triggerid=t.triggerid'.
371						' AND t.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
372					')';
373			}
374			else {
375				$sqlParts['where'][] = 'NOT EXISTS ('.
376					'SELECT NULL'.
377					' FROM functions ff,triggers t'.
378					' WHERE i.itemid=ff.itemid'.
379						' AND ff.triggerid=t.triggerid'.
380						' AND t.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
381					')';
382			}
383		}
384
385		// limit
386		if (zbx_ctype_digit($options['limit']) && $options['limit']) {
387			$sqlParts['limit'] = $options['limit'];
388		}
389
390		$sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
391		$sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
392		$res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']);
393		while ($item = DBfetch($res)) {
394			// Items share table with item prototypes. Therefore remove item unrelated fields.
395			unset($item['discover']);
396
397			if (!$options['countOutput']) {
398				$result[$item['itemid']] = $item;
399				continue;
400			}
401
402			if ($options['groupCount']) {
403				$result[] = $item;
404			}
405			else {
406				$result = $item['rowscount'];
407			}
408		}
409
410		if ($options['countOutput']) {
411			return $result;
412		}
413
414		if ($result) {
415			if (self::dbDistinct($sqlParts)) {
416				$result = $this->addNclobFieldValues($options, $result);
417			}
418
419			$result = $this->addRelatedObjects($options, $result);
420			$result = $this->unsetExtraFields($result, ['hostid', 'interfaceid', 'value_type', 'valuemapid'],
421				$options['output']
422			);
423		}
424
425		// removing keys (hash -> array)
426		if (!$options['preservekeys']) {
427			$result = zbx_cleanHashes($result);
428		}
429
430		// Decode ITEM_TYPE_HTTPAGENT encoded fields.
431		foreach ($result as &$item) {
432			if (array_key_exists('query_fields', $item)) {
433				$query_fields = ($item['query_fields'] !== '') ? json_decode($item['query_fields'], true) : [];
434				$item['query_fields'] = json_last_error() ? [] : $query_fields;
435			}
436
437			if (array_key_exists('headers', $item)) {
438				$item['headers'] = $this->headersStringToArray($item['headers']);
439			}
440		}
441		unset($item);
442
443		return $result;
444	}
445
446	/**
447	 * Validates the input parameters for the get() method.
448	 *
449	 * @param array $options
450	 *
451	 * @throws APIException if the input is invalid
452	 */
453	private function validateGet(array $options) {
454		// Validate input parameters.
455		$api_input_rules = ['type' => API_OBJECT, 'fields' => [
456			'selectValueMap' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => 'valuemapid,name,mappings'],
457			'evaltype' => ['type' => API_INT32, 'in' => implode(',', [TAG_EVAL_TYPE_AND_OR, TAG_EVAL_TYPE_OR])]
458		]];
459		$options_filter = array_intersect_key($options, $api_input_rules['fields']);
460		if (!CApiInputValidator::validate($api_input_rules, $options_filter, '/', $error)) {
461			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
462		}
463	}
464
465	/**
466	 * Create item.
467	 *
468	 * @param $items
469	 *
470	 * @return array
471	 */
472	public function create($items) {
473		$items = zbx_toArray($items);
474
475		parent::checkInput($items);
476		self::validateInventoryLinks($items);
477
478		foreach ($items as &$item) {
479			$item['flags'] = ZBX_FLAG_DISCOVERY_NORMAL;
480			unset($item['itemid']);
481		}
482		unset($item);
483
484		$this->validateDependentItems($items);
485
486		foreach ($items as &$item) {
487			if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
488				if (array_key_exists('query_fields', $item)) {
489					$item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
490				}
491
492				if (array_key_exists('headers', $item)) {
493					$item['headers'] = $this->headersArrayToString($item['headers']);
494				}
495
496				if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
497						&& !array_key_exists('retrieve_mode', $item)) {
498					$item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
499				}
500			}
501			else {
502				$item['query_fields'] = '';
503				$item['headers'] = '';
504			}
505		}
506		unset($item);
507
508		// Get only hosts not templates from items
509		$hosts = API::Host()->get([
510			'output' => [],
511			'hostids' => zbx_objectValues($items, 'hostid'),
512			'preservekeys' => true
513		]);
514		foreach ($items as &$item) {
515			if (array_key_exists($item['hostid'], $hosts)) {
516				$item['rtdata'] = true;
517			}
518		}
519		unset($item);
520
521		$this->createReal($items);
522		$this->inherit($items);
523
524		return ['itemids' => zbx_objectValues($items, 'itemid')];
525	}
526
527	/**
528	 * Create host item.
529	 *
530	 * @param array $items
531	 */
532	protected function createReal(array &$items) {
533		$items_rtdata = [];
534
535		foreach ($items as $key => &$item) {
536			if ($item['type'] != ITEM_TYPE_DEPENDENT) {
537				$item['master_itemid'] = null;
538			}
539
540			if (array_key_exists('rtdata', $item)) {
541				$items_rtdata[$key] = [];
542				unset($item['rtdata']);
543			}
544		}
545		unset($item);
546
547		$itemids = DB::insert('items', $items);
548
549		foreach ($items_rtdata as $key => &$value) {
550			$value['itemid'] = $itemids[$key];
551		}
552		unset($value);
553
554		DB::insert('item_rtdata', $items_rtdata, false);
555
556		foreach ($items as $key => $item) {
557			$items[$key]['itemid'] = $itemids[$key];
558		}
559
560		$this->createItemParameters($items, $itemids);
561		$this->createItemPreprocessing($items);
562		$this->createItemTags($items, $itemids);
563	}
564
565	/**
566	 * Update host items.
567	 *
568	 * @param array $items
569	 */
570	protected function updateReal(array $items) {
571		CArrayHelper::sort($items, ['itemid']);
572
573		$data = [];
574		foreach ($items as $item) {
575			unset($item['flags']); // flags cannot be changed
576			$data[] = ['values' => $item, 'where' => ['itemid' => $item['itemid']]];
577		}
578		DB::update('items', $data);
579
580		$this->updateItemParameters($items);
581		$this->updateItemPreprocessing($items);
582		$this->updateItemTags($items);
583	}
584
585	/**
586	 * Update item.
587	 *
588	 * @param array $items
589	 *
590	 * @return array
591	 */
592	public function update($items) {
593		$items = zbx_toArray($items);
594
595		parent::checkInput($items, true);
596		self::validateInventoryLinks($items, true);
597
598		$db_items = $this->get([
599			'output' => ['flags', 'type', 'master_itemid', 'authtype', 'allow_traps', 'retrieve_mode', 'value_type'],
600			'itemids' => zbx_objectValues($items, 'itemid'),
601			'editable' => true,
602			'preservekeys' => true
603		]);
604
605		$items = $this->extendFromObjects(zbx_toHash($items, 'itemid'), $db_items, ['flags', 'type', 'authtype',
606			'master_itemid', 'value_type'
607		]);
608
609		$this->validateDependentItems($items);
610
611		$defaults = DB::getDefaults('items');
612		$clean = [
613			ITEM_TYPE_HTTPAGENT => [
614				'url' => '',
615				'query_fields' => '',
616				'timeout' => $defaults['timeout'],
617				'status_codes' => $defaults['status_codes'],
618				'follow_redirects' => $defaults['follow_redirects'],
619				'request_method' => $defaults['request_method'],
620				'allow_traps' => $defaults['allow_traps'],
621				'post_type' => $defaults['post_type'],
622				'http_proxy' => '',
623				'headers' => '',
624				'retrieve_mode' => $defaults['retrieve_mode'],
625				'output_format' => $defaults['output_format'],
626				'ssl_key_password' => '',
627				'verify_peer' => $defaults['verify_peer'],
628				'verify_host' => $defaults['verify_host'],
629				'ssl_cert_file' => '',
630				'ssl_key_file' => '',
631				'posts' => ''
632			]
633		];
634
635		foreach ($items as &$item) {
636			$type_change = ($item['type'] != $db_items[$item['itemid']]['type']);
637
638			if ($item['type'] != ITEM_TYPE_DEPENDENT && $db_items[$item['itemid']]['master_itemid'] != 0) {
639				$item['master_itemid'] = 0;
640			}
641
642			if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_HTTPAGENT) {
643				$item = array_merge($item, $clean[ITEM_TYPE_HTTPAGENT]);
644
645				if ($item['type'] != ITEM_TYPE_SSH) {
646					$item['authtype'] = $defaults['authtype'];
647					$item['username'] = '';
648					$item['password'] = '';
649				}
650
651				if ($item['type'] != ITEM_TYPE_TRAPPER) {
652					$item['trapper_hosts'] = '';
653				}
654			}
655
656			if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
657				// Clean username and password when authtype is set to HTTPTEST_AUTH_NONE.
658				if ($item['authtype'] == HTTPTEST_AUTH_NONE) {
659					$item['username'] = '';
660					$item['password'] = '';
661				}
662
663				if (array_key_exists('allow_traps', $item) && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF
664						&& $item['allow_traps'] != $db_items[$item['itemid']]['allow_traps']) {
665					$item['trapper_hosts'] = '';
666				}
667
668				if (array_key_exists('query_fields', $item) && is_array($item['query_fields'])) {
669					$item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
670				}
671
672				if (array_key_exists('headers', $item) && is_array($item['headers'])) {
673					$item['headers'] = $this->headersArrayToString($item['headers']);
674				}
675
676				if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
677						&& !array_key_exists('retrieve_mode', $item)
678						&& $db_items[$item['itemid']]['retrieve_mode'] != HTTPTEST_STEP_RETRIEVE_MODE_HEADERS) {
679					$item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
680				}
681			}
682			else {
683				$item['query_fields'] = '';
684				$item['headers'] = '';
685			}
686
687			if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT) {
688				if ($item['type'] != ITEM_TYPE_SSH && $item['type'] != ITEM_TYPE_DB_MONITOR
689						&& $item['type'] != ITEM_TYPE_TELNET && $item['type'] != ITEM_TYPE_CALCULATED) {
690					$item['params'] = '';
691				}
692
693				if ($item['type'] != ITEM_TYPE_HTTPAGENT) {
694					$item['timeout'] = $defaults['timeout'];
695				}
696			}
697
698			if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
699				if ($item['value_type'] != $db_items[$item['itemid']]['value_type']) {
700					// Reset valuemapid when value_type is LOG or TEXT.
701					$item['valuemapid'] = 0;
702				}
703				else {
704					unset($item['valuemapid']);
705				}
706			}
707
708			if (array_key_exists('tags', $item)) {
709				$item['tags'] = array_map(function ($tag) {
710					return $tag + ['value' => ''];
711				}, $item['tags']);
712			}
713		}
714		unset($item);
715
716		$this->updateReal($items);
717		$this->inherit($items);
718
719		return ['itemids' => zbx_objectValues($items, 'itemid')];
720	}
721
722	/**
723	 * Delete items.
724	 *
725	 * @param array $itemids
726	 *
727	 * @return array
728	 */
729	public function delete(array $itemids) {
730		$this->validateDelete($itemids, $db_items);
731
732		CItemManager::delete($itemids);
733
734		$this->addAuditBulk(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_ITEM, $db_items);
735
736		return ['itemids' => $itemids];
737	}
738
739	/**
740	 * Validates the input parameters for the delete() method.
741	 *
742	 * @param array $itemids   [IN/OUT]
743	 * @param array $db_items  [OUT]
744	 *
745	 * @throws APIException if the input is invalid.
746	 */
747	private function validateDelete(array &$itemids, array &$db_items = null) {
748		$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
749		if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
750			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
751		}
752
753		$db_items = $this->get([
754			'output' => ['itemid', 'name', 'templateid', 'flags'],
755			'itemids' => $itemids,
756			'editable' => true,
757			'preservekeys' => true
758		]);
759
760		foreach ($itemids as $itemid) {
761			if (!array_key_exists($itemid, $db_items)) {
762				self::exception(ZBX_API_ERROR_PERMISSIONS,
763					_('No permissions to referred object or it does not exist!')
764				);
765			}
766
767			$db_item = $db_items[$itemid];
768
769			if ($db_item['templateid'] != 0) {
770				self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete templated item.'));
771			}
772		}
773	}
774
775	public function syncTemplates($data) {
776		$data['templateids'] = zbx_toArray($data['templateids']);
777		$data['hostids'] = zbx_toArray($data['hostids']);
778
779		$output = [];
780		foreach ($this->fieldRules as $field_name => $rules) {
781			if (!array_key_exists('system', $rules) && !array_key_exists('host', $rules)) {
782				$output[] = $field_name;
783			}
784		}
785
786		$tpl_items = $this->get([
787			'output' => $output,
788			'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
789			'selectTags' => ['tag', 'value'],
790			'hostids' => $data['templateids'],
791			'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
792			'preservekeys' => true
793		]);
794
795		foreach ($tpl_items as &$tpl_item) {
796			if ($tpl_item['type'] == ITEM_TYPE_HTTPAGENT) {
797				if (array_key_exists('query_fields', $tpl_item) && is_array($tpl_item['query_fields'])) {
798					$tpl_item['query_fields'] = $tpl_item['query_fields']
799						? json_encode($tpl_item['query_fields'])
800						: '';
801				}
802
803				if (array_key_exists('headers', $tpl_item) && is_array($tpl_item['headers'])) {
804					$tpl_item['headers'] = $this->headersArrayToString($tpl_item['headers']);
805				}
806			}
807			else {
808				$tpl_item['query_fields'] = '';
809				$tpl_item['headers'] = '';
810			}
811		}
812		unset($tpl_item);
813
814		$this->inherit($tpl_items, $data['hostids']);
815
816		return true;
817	}
818
819	/**
820	 * Check item specific fields:
821	 *		- validate history and trends using simple interval parser and user macro parser;
822	 *		- validate item preprocessing.
823	 *
824	 * @param array  $item    An array of single item data.
825	 * @param string $method  A string of "create" or "update" method.
826	 *
827	 * @throws APIException if the input is invalid.
828	 */
829	protected function checkSpecificFields(array $item, $method) {
830		if (array_key_exists('history', $item)
831				&& !validateTimeUnit($item['history'], SEC_PER_HOUR, 25 * SEC_PER_YEAR, true, $error,
832					['usermacros' => true])) {
833			self::exception(ZBX_API_ERROR_PARAMETERS,
834				_s('Incorrect value for field "%1$s": %2$s.', 'history', $error)
835			);
836		}
837
838		if (array_key_exists('trends', $item)
839				&& !validateTimeUnit($item['trends'], SEC_PER_DAY, 25 * SEC_PER_YEAR, true, $error,
840					['usermacros' => true])) {
841			self::exception(ZBX_API_ERROR_PARAMETERS,
842				_s('Incorrect value for field "%1$s": %2$s.', 'trends', $error)
843			);
844		}
845	}
846
847	/**
848	 * Check, if items that are about to be inserted or updated violate the rule:
849	 * only one item can be linked to a inventory filed.
850	 * If everything is ok, function return true or throws Exception otherwise
851	 *
852	 * @static
853	 *
854	 * @param array $items
855	 * @param bool $update whether this is update operation
856	 *
857	 * @return bool
858	 */
859	public static function validateInventoryLinks(array $items, $update = false) {
860		// inventory link field is not being updated, or being updated to 0, no need to validate anything then
861		foreach ($items as $i => $item) {
862			if (!isset($item['inventory_link']) || $item['inventory_link'] == 0) {
863				unset($items[$i]);
864			}
865		}
866
867		if (zbx_empty($items)) {
868			return true;
869		}
870
871		$possibleHostInventories = getHostInventories();
872		if ($update) {
873			// for successful validation we need three fields for each item: inventory_link, hostid and key_
874			// problem is, that when we are updating an item, we might not have them, because they are not changed
875			// so, we need to find out what is missing and use API to get the lacking info
876			$itemsWithNoHostId = [];
877			$itemsWithNoInventoryLink = [];
878			$itemsWithNoKeys = [];
879			foreach ($items as $item) {
880				if (!isset($item['inventory_link'])) {
881					$itemsWithNoInventoryLink[$item['itemid']] = $item['itemid'];
882				}
883				if (!isset($item['hostid'])) {
884					$itemsWithNoHostId[$item['itemid']] = $item['itemid'];
885				}
886				if (!isset($item['key_'])) {
887					$itemsWithNoKeys[$item['itemid']] = $item['itemid'];
888				}
889			}
890			$itemsToFind = array_merge($itemsWithNoHostId, $itemsWithNoInventoryLink, $itemsWithNoKeys);
891
892			// are there any items with lacking info?
893			if (!zbx_empty($itemsToFind)) {
894				$missingInfo = API::Item()->get([
895					'output' => ['hostid', 'inventory_link', 'key_'],
896					'filter' => ['itemid' => $itemsToFind],
897					'nopermissions' => true
898				]);
899				$missingInfo = zbx_toHash($missingInfo, 'itemid');
900
901				// appending host ids, inventory_links and keys where they are needed
902				foreach ($items as $i => $item) {
903					if (isset($missingInfo[$item['itemid']])) {
904						if (!isset($items[$i]['hostid'])) {
905							$items[$i]['hostid'] = $missingInfo[$item['itemid']]['hostid'];
906						}
907						if (!isset($items[$i]['inventory_link'])) {
908							$items[$i]['inventory_link'] = $missingInfo[$item['itemid']]['inventory_link'];
909						}
910						if (!isset($items[$i]['key_'])) {
911							$items[$i]['key_'] = $missingInfo[$item['itemid']]['key_'];
912						}
913					}
914				}
915			}
916		}
917
918		$hostids = zbx_objectValues($items, 'hostid');
919
920		// getting all inventory links on every affected host
921		$itemsOnHostsInfo = API::Item()->get([
922			'output' => ['key_', 'inventory_link', 'hostid'],
923			'filter' => ['hostid' => $hostids],
924			'nopermissions' => true
925		]);
926
927		// now, changing array to: 'hostid' => array('key_'=>'inventory_link')
928		$linksOnHostsCurr = [];
929		foreach ($itemsOnHostsInfo as $info) {
930			// 0 means no link - we are not interested in those ones
931			if ($info['inventory_link'] != 0) {
932				if (!isset($linksOnHostsCurr[$info['hostid']])) {
933					$linksOnHostsCurr[$info['hostid']] = [$info['key_'] => $info['inventory_link']];
934				}
935				else{
936					$linksOnHostsCurr[$info['hostid']][$info['key_']] = $info['inventory_link'];
937				}
938			}
939		}
940
941		$linksOnHostsFuture = [];
942
943		foreach ($items as $item) {
944			// checking if inventory_link value is a valid number
945			if ($update || $item['value_type'] != ITEM_VALUE_TYPE_LOG) {
946				// does inventory field with provided number exists?
947				if (!isset($possibleHostInventories[$item['inventory_link']])) {
948					$maxVar = max(array_keys($possibleHostInventories));
949					self::exception(
950						ZBX_API_ERROR_PARAMETERS,
951						_s('Item "%1$s" cannot populate a missing host inventory field number "%2$d". Choices are: from 0 (do not populate) to %3$d.', $item['name'], $item['inventory_link'], $maxVar)
952					);
953				}
954			}
955
956			if (!isset($linksOnHostsFuture[$item['hostid']])) {
957				$linksOnHostsFuture[$item['hostid']] = [$item['key_'] => $item['inventory_link']];
958			}
959			else {
960				$linksOnHostsFuture[$item['hostid']][$item['key_']] = $item['inventory_link'];
961			}
962		}
963
964		foreach ($linksOnHostsFuture as $hostId => $linkFuture) {
965			if (isset($linksOnHostsCurr[$hostId])) {
966				$futureSituation = array_merge($linksOnHostsCurr[$hostId], $linksOnHostsFuture[$hostId]);
967			}
968			else {
969				$futureSituation = $linksOnHostsFuture[$hostId];
970			}
971			$valuesCount = array_count_values($futureSituation);
972
973			// if we have a duplicate inventory links after merging - we are in trouble
974			if (max($valuesCount) > 1) {
975				// what inventory field caused this conflict?
976				$conflictedLink = array_keys($valuesCount, 2);
977				$conflictedLink = reset($conflictedLink);
978
979				// which of updated items populates this link?
980				$beingSavedItemName = '';
981				foreach ($items as $item) {
982					if ($item['inventory_link'] == $conflictedLink) {
983						if (isset($item['name'])) {
984							$beingSavedItemName = $item['name'];
985						}
986						else {
987							$thisItem = API::Item()->get([
988								'output' => ['name'],
989								'filter' => ['itemid' => $item['itemid']],
990								'nopermissions' => true
991							]);
992							$beingSavedItemName = $thisItem[0]['name'];
993						}
994						break;
995					}
996				}
997
998				// name of the original item that already populates the field
999				$originalItem = API::Item()->get([
1000					'output' => ['name'],
1001					'filter' => [
1002						'hostid' => $hostId,
1003						'inventory_link' => $conflictedLink
1004					],
1005					'nopermissions' => true
1006				]);
1007				$originalItemName = $originalItem[0]['name'];
1008
1009				self::exception(
1010					ZBX_API_ERROR_PARAMETERS,
1011					_s(
1012						'Two items ("%1$s" and "%2$s") cannot populate one host inventory field "%3$s", this would lead to a conflict.',
1013						$beingSavedItemName,
1014						$originalItemName,
1015						$possibleHostInventories[$conflictedLink]['title']
1016					)
1017				);
1018			}
1019		}
1020
1021		return true;
1022	}
1023
1024	public function addRelatedObjects(array $options, array $result) {
1025		$result = parent::addRelatedObjects($options, $result);
1026
1027		$itemids = array_keys($result);
1028
1029		// adding interfaces
1030		if ($options['selectInterfaces'] !== null && $options['selectInterfaces'] != API_OUTPUT_COUNT) {
1031			$relationMap = $this->createRelationMap($result, 'itemid', 'interfaceid');
1032			$interfaces = API::HostInterface()->get([
1033				'output' => $options['selectInterfaces'],
1034				'interfaceids' => $relationMap->getRelatedIds(),
1035				'nopermissions' => true,
1036				'preservekeys' => true
1037			]);
1038			$result = $relationMap->mapMany($result, $interfaces, 'interfaces');
1039		}
1040
1041		// adding triggers
1042		if (!is_null($options['selectTriggers'])) {
1043			if ($options['selectTriggers'] != API_OUTPUT_COUNT) {
1044				$triggers = [];
1045				$relationMap = $this->createRelationMap($result, 'itemid', 'triggerid', 'functions');
1046				$related_ids = $relationMap->getRelatedIds();
1047
1048				if ($related_ids) {
1049					$triggers = API::Trigger()->get([
1050						'output' => $options['selectTriggers'],
1051						'triggerids' => $related_ids,
1052						'preservekeys' => true
1053					]);
1054
1055					if (!is_null($options['limitSelects'])) {
1056						order_result($triggers, 'description');
1057					}
1058				}
1059
1060				$result = $relationMap->mapMany($result, $triggers, 'triggers', $options['limitSelects']);
1061			}
1062			else {
1063				$triggers = API::Trigger()->get([
1064					'countOutput' => true,
1065					'groupCount' => true,
1066					'itemids' => $itemids
1067				]);
1068				$triggers = zbx_toHash($triggers, 'itemid');
1069
1070				foreach ($result as $itemid => $item) {
1071					$result[$itemid]['triggers'] = array_key_exists($itemid, $triggers)
1072						? $triggers[$itemid]['rowscount']
1073						: '0';
1074				}
1075			}
1076		}
1077
1078		// adding graphs
1079		if (!is_null($options['selectGraphs'])) {
1080			if ($options['selectGraphs'] != API_OUTPUT_COUNT) {
1081				$graphs = [];
1082				$relationMap = $this->createRelationMap($result, 'itemid', 'graphid', 'graphs_items');
1083				$related_ids = $relationMap->getRelatedIds();
1084
1085				if ($related_ids) {
1086					$graphs = API::Graph()->get([
1087						'output' => $options['selectGraphs'],
1088						'graphids' => $related_ids,
1089						'preservekeys' => true
1090					]);
1091
1092					if (!is_null($options['limitSelects'])) {
1093						order_result($graphs, 'name');
1094					}
1095				}
1096
1097				$result = $relationMap->mapMany($result, $graphs, 'graphs', $options['limitSelects']);
1098			}
1099			else {
1100				$graphs = API::Graph()->get([
1101					'countOutput' => true,
1102					'groupCount' => true,
1103					'itemids' => $itemids
1104				]);
1105				$graphs = zbx_toHash($graphs, 'itemid');
1106
1107				foreach ($result as $itemid => $item) {
1108					$result[$itemid]['graphs'] = array_key_exists($itemid, $graphs)
1109						? $graphs[$itemid]['rowscount']
1110						: '0';
1111				}
1112			}
1113		}
1114
1115		// adding discoveryrule
1116		if ($options['selectDiscoveryRule'] !== null && $options['selectDiscoveryRule'] != API_OUTPUT_COUNT) {
1117			$discoveryRules = [];
1118			$relationMap = new CRelationMap();
1119			// discovered items
1120			$dbRules = DBselect(
1121				'SELECT id1.itemid,id2.parent_itemid'.
1122					' FROM item_discovery id1,item_discovery id2,items i'.
1123					' WHERE '.dbConditionInt('id1.itemid', $itemids).
1124					' AND id1.parent_itemid=id2.itemid'.
1125					' AND i.itemid=id1.itemid'.
1126					' AND i.flags='.ZBX_FLAG_DISCOVERY_CREATED
1127			);
1128			while ($rule = DBfetch($dbRules)) {
1129				$relationMap->addRelation($rule['itemid'], $rule['parent_itemid']);
1130			}
1131
1132			// item prototypes
1133			// TODO: this should not be in the item API
1134			$dbRules = DBselect(
1135				'SELECT id.parent_itemid,id.itemid'.
1136					' FROM item_discovery id,items i'.
1137					' WHERE '.dbConditionInt('id.itemid', $itemids).
1138					' AND i.itemid=id.itemid'.
1139					' AND i.flags='.ZBX_FLAG_DISCOVERY_PROTOTYPE
1140			);
1141			while ($rule = DBfetch($dbRules)) {
1142				$relationMap->addRelation($rule['itemid'], $rule['parent_itemid']);
1143			}
1144
1145			$related_ids = $relationMap->getRelatedIds();
1146
1147			if ($related_ids) {
1148				$discoveryRules = API::DiscoveryRule()->get([
1149					'output' => $options['selectDiscoveryRule'],
1150					'itemids' => $related_ids,
1151					'nopermissions' => true,
1152					'preservekeys' => true
1153				]);
1154			}
1155
1156			$result = $relationMap->mapOne($result, $discoveryRules, 'discoveryRule');
1157		}
1158
1159		// adding item discovery
1160		if ($options['selectItemDiscovery'] !== null) {
1161			$itemDiscoveries = API::getApiService()->select('item_discovery', [
1162				'output' => $this->outputExtend($options['selectItemDiscovery'], ['itemdiscoveryid', 'itemid']),
1163				'filter' => ['itemid' => array_keys($result)],
1164				'preservekeys' => true
1165			]);
1166			$relationMap = $this->createRelationMap($itemDiscoveries, 'itemid', 'itemdiscoveryid');
1167
1168			$itemDiscoveries = $this->unsetExtraFields($itemDiscoveries, ['itemid', 'itemdiscoveryid'],
1169				$options['selectItemDiscovery']
1170			);
1171			$result = $relationMap->mapOne($result, $itemDiscoveries, 'itemDiscovery');
1172		}
1173
1174		// adding history data
1175		$requestedOutput = [];
1176		if ($this->outputIsRequested('lastclock', $options['output'])) {
1177			$requestedOutput['lastclock'] = true;
1178		}
1179		if ($this->outputIsRequested('lastns', $options['output'])) {
1180			$requestedOutput['lastns'] = true;
1181		}
1182		if ($this->outputIsRequested('lastvalue', $options['output'])) {
1183			$requestedOutput['lastvalue'] = true;
1184		}
1185		if ($this->outputIsRequested('prevvalue', $options['output'])) {
1186			$requestedOutput['prevvalue'] = true;
1187		}
1188		if ($requestedOutput) {
1189			$history = Manager::History()->getLastValues($result, 2, timeUnitToSeconds(CSettingsHelper::get(
1190				CSettingsHelper::HISTORY_PERIOD
1191			)));
1192			foreach ($result as &$item) {
1193				$lastHistory = isset($history[$item['itemid']][0]) ? $history[$item['itemid']][0] : null;
1194				$prevHistory = isset($history[$item['itemid']][1]) ? $history[$item['itemid']][1] : null;
1195				$no_value = in_array($item['value_type'],
1196						[ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT]) ? '' : '0';
1197
1198				if (isset($requestedOutput['lastclock'])) {
1199					$item['lastclock'] = $lastHistory ? $lastHistory['clock'] : '0';
1200				}
1201				if (isset($requestedOutput['lastns'])) {
1202					$item['lastns'] = $lastHistory ? $lastHistory['ns'] : '0';
1203				}
1204				if (isset($requestedOutput['lastvalue'])) {
1205					$item['lastvalue'] = $lastHistory ? $lastHistory['value'] : $no_value;
1206				}
1207				if (isset($requestedOutput['prevvalue'])) {
1208					$item['prevvalue'] = $prevHistory ? $prevHistory['value'] : $no_value;
1209				}
1210			}
1211			unset($item);
1212		}
1213
1214		// Adding item tags.
1215		if ($options['selectTags'] !== null) {
1216			$options['selectTags'] = ($options['selectTags'] !== API_OUTPUT_EXTEND)
1217				? (array) $options['selectTags']
1218				: ['tag', 'value'];
1219
1220			$options['selectTags'] = array_intersect(['tag', 'value'], $options['selectTags']);
1221			$requested_output = array_flip($options['selectTags']);
1222
1223			$db_tags = DBselect(
1224				'SELECT '.implode(',', array_merge($options['selectTags'], ['itemid'])).
1225				' FROM item_tag'.
1226				' WHERE '.dbConditionInt('itemid', $itemids)
1227			);
1228
1229			array_walk($result, function (&$item) {
1230				$item['tags'] = [];
1231			});
1232
1233			while ($db_tag = DBfetch($db_tags)) {
1234				$result[$db_tag['itemid']]['tags'][] = array_intersect_key($db_tag, $requested_output);
1235			}
1236		}
1237
1238		return $result;
1239	}
1240
1241	protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) {
1242		$sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts);
1243
1244		if ((!$options['countOutput'] && ($this->outputIsRequested('state', $options['output'])
1245				|| $this->outputIsRequested('error', $options['output'])))
1246				|| (is_array($options['search']) && array_key_exists('error', $options['search']))
1247				|| (is_array($options['filter']) && array_key_exists('state', $options['filter']))) {
1248			$sqlParts['left_join'][] = ['alias' => 'ir', 'table' => 'item_rtdata', 'using' => 'itemid'];
1249			$sqlParts['left_table'] = ['alias' => $this->tableAlias, 'table' => $this->tableName];
1250		}
1251
1252		if (!$options['countOutput']) {
1253			if ($this->outputIsRequested('state', $options['output'])) {
1254				$sqlParts = $this->addQuerySelect('ir.state', $sqlParts);
1255			}
1256			if ($this->outputIsRequested('error', $options['output'])) {
1257				/*
1258				 * SQL func COALESCE use for template items because they don't have record
1259				 * in item_rtdata table and DBFetch convert null to '0'
1260				 */
1261				$sqlParts = $this->addQuerySelect(dbConditionCoalesce('ir.error', '', 'error'), $sqlParts);
1262			}
1263
1264			if ($options['selectHosts'] !== null) {
1265				$sqlParts = $this->addQuerySelect('i.hostid', $sqlParts);
1266			}
1267
1268			if ($options['selectInterfaces'] !== null) {
1269				$sqlParts = $this->addQuerySelect('i.interfaceid', $sqlParts);
1270			}
1271
1272			if ($options['selectValueMap'] !== null) {
1273				$sqlParts = $this->addQuerySelect('i.valuemapid', $sqlParts);
1274			}
1275
1276			if ($this->outputIsRequested('lastclock', $options['output'])
1277					|| $this->outputIsRequested('lastns', $options['output'])
1278					|| $this->outputIsRequested('lastvalue', $options['output'])
1279					|| $this->outputIsRequested('prevvalue', $options['output'])) {
1280
1281				$sqlParts = $this->addQuerySelect('i.value_type', $sqlParts);
1282			}
1283		}
1284
1285		return $sqlParts;
1286	}
1287}
1288