/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "zbxdbhigh.h"

#include "template.h"

#include "zbxcacheconfig.h"
#include "zbxdb.h"
#include "audit/zbxaudit.h"
#include "audit/zbxaudit_item.h"

#include "zbxalgo.h"
#include "zbxstr.h"
#include "zbxnum.h"
#include "zbxinterface.h"
#include "zbx_host_constants.h"
#include "zbx_trigger_constants.h"

struct _zbx_template_item_preproc_t
{
	zbx_uint64_t	item_preprocid;
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_RESET_FLAG			__UINT64_C(0x000000000000)
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_TYPE			__UINT64_C(0x000000000001)
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_PARAMS			__UINT64_C(0x000000000002)
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER		__UINT64_C(0x000000000004)
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER_PARAMS	__UINT64_C(0x000000000008)
#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE					\
		(ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_TYPE |			\
		ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_PARAMS |			\
		ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER |		\
		ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER_PARAMS	\
		)

#define ZBX_FLAG_TEMPLATE_ITEM_PREPROC_DELETE				__UINT64_C(0x000000010000)

	zbx_uint64_t	upd_flags;
	int		step;
	int		type_orig;
	int		type;
	char		*params_orig;
	char		*params;
	int		error_handler_orig;
	int		error_handler;
	char		*error_handler_params_orig;
	char		*error_handler_params;
};

ZBX_PTR_VECTOR_IMPL(item_preproc_ptr, zbx_template_item_preproc_t *)

struct _zbx_template_lld_macro_t
{
	zbx_uint64_t	lld_macro_pathid;
#define ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_RESET_FLAG	__UINT64_C(0x000000000000)
#define ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_LLD_MACRO	__UINT64_C(0x000000000001)
#define ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_PATH		__UINT64_C(0x000000000002)
#define ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE			\
		(ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_LLD_MACRO |	\
		ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_PATH		\
		)

#define ZBX_FLAG_TEMPLATE_LLD_MACRO_DELETE		__UINT64_C(0x000000010000)

	zbx_uint64_t	upd_flags;
	char		*lld_macro_orig;
	char		*lld_macro;
	char		*path_orig;
	char		*path;
};

ZBX_PTR_VECTOR_IMPL(lld_macro_ptr, zbx_template_lld_macro_t *)

/* lld rule condition */
typedef struct
{
	zbx_uint64_t	item_conditionid;
#define ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_RESET_FLAG	__UINT64_C(0x00000000)
#define ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_MACRO		__UINT64_C(0x00000001)
#define ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_VALUE		__UINT64_C(0x00000002)
#define ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_OPERATOR	__UINT64_C(0x00000004)
	zbx_uint64_t	upd_flags;
	char		*macro_orig;
	char		*macro;
	char		*value_orig;
	char		*value;
	unsigned char	op_orig;
	unsigned char	op;
}
zbx_lld_rule_condition_t;

/* lld rule */
typedef struct
{
	/* discovery rule source id */
	zbx_uint64_t		templateid;
	/* discovery rule source conditions */
	zbx_vector_ptr_t	conditions;

	/* discovery rule destination id */
	zbx_uint64_t		itemid;
	/* the starting id to be used for destination condition ids */
	zbx_uint64_t		conditionid;
	/* discovery rule destination condition ids */
	zbx_vector_uint64_t	conditionids;
}
zbx_lld_rule_map_t;

typedef struct
{
	zbx_uint64_t				overrideid;
	zbx_uint64_t				itemid;
	char					*name;
	char					*formula;
	zbx_vector_ptr_t			override_conditions;
	zbx_vector_lld_override_operation_t	override_operations;
	unsigned char				step;
	unsigned char				evaltype;
	unsigned char				stop;
}
lld_override_t;

typedef struct
{
	zbx_uint64_t		override_conditionid;
	char			*macro;
	char			*value;
	unsigned char		operator;
}
lld_override_codition_t;

/* auxiliary function for DBcopy_template_items() */
static void	DBget_interfaces_by_hostid(zbx_uint64_t hostid, zbx_uint64_t *interfaceids)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	unsigned char	type;

	result = zbx_db_select(
			"select type,interfaceid"
			" from interface"
			" where hostid=" ZBX_FS_UI64
				" and type in (%d,%d,%d,%d)"
				" and main=1",
			hostid, INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_IPMI, INTERFACE_TYPE_JMX);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_STR2UCHAR(type, row[0]);
		ZBX_STR2UINT64(interfaceids[type - 1], row[1]);
	}
	zbx_db_free_result(result);
}

/******************************************************************************
 *                                                                            *
 * Purpose: read template items from database                                 *
 *                                                                            *
 * Parameters: hostid      - [IN] host id                                     *
 *             templateids - [IN] array of template IDs                       *
 *             items       - [OUT] the item data                              *
 *                                                                            *
 * Comments: The itemid and key are set depending on whether the item exists  *
 *           for the specified host.                                          *
 *           If item exists itemid will be set to its itemid and key will be  *
 *           set to NULL.                                                     *
 *           If item does not exist, itemid will be set to 0 and key will be  *
 *           set to item key.                                                 *
 *                                                                            *
 ******************************************************************************/
static void	get_template_items(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids, zbx_vector_ptr_t *items)
{
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0, i;
	unsigned char		interface_type;
	zbx_template_item_t	*item;
	zbx_uint64_t		interfaceids[4];

	memset(&interfaceids, 0, sizeof(interfaceids));
	DBget_interfaces_by_hostid(hostid, interfaceids);

	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
			"select ti.itemid,ti.name,ti.key_,ti.type,ti.value_type,ti.delay,ti.history,ti.trends,"
				"ti.status,ti.trapper_hosts,ti.units,ti.formula,ti.logtimefmt,ti.valuemapid,"
				"ti.params,ti.ipmi_sensor,ti.snmp_oid,ti.authtype,ti.username,ti.password,"
				"ti.publickey,ti.privatekey,ti.flags,ti.description,ti.inventory_link,ti.lifetime,"
				"hi.itemid,ti.evaltype,ti.jmx_endpoint,ti.master_itemid,ti.timeout,ti.url,"
				"ti.query_fields,ti.posts,ti.status_codes,ti.follow_redirects,ti.post_type,"
				"ti.http_proxy,ti.headers,ti.retrieve_mode,ti.request_method,ti.output_format,"
				"ti.ssl_cert_file,ti.ssl_key_file,ti.ssl_key_password,ti.verify_peer,ti.verify_host,"
				"ti.allow_traps,ti.discover,ti.lifetime_type,ti.enabled_lifetime,"
				"ti.enabled_lifetime_type,"
				"hi.interfaceid,hi.templateid,hi.name,hi.type,hi.value_type,hi.delay,hi.history,"
				"hi.trends,hi.status,hi.trapper_hosts,hi.units,hi.formula,hi.logtimefmt,hi.valuemapid,"
				"hi.params,hi.ipmi_sensor,hi.snmp_oid,hi.authtype,hi.username,hi.password,hi.publickey,"
				"hi.privatekey,hi.flags,hi.description,hi.inventory_link,hi.lifetime,hi.evaltype,"
				"hi.jmx_endpoint,hi.master_itemid,hi.timeout,hi.url,hi.query_fields,hi.posts,"
				"hi.status_codes,hi.follow_redirects,hi.post_type,hi.http_proxy,hi.headers,"
				"hi.retrieve_mode,hi.request_method,hi.output_format,hi.ssl_cert_file,hi.ssl_key_file,"
				"hi.ssl_key_password,hi.verify_peer,hi.verify_host,hi.allow_traps,hi.discover,"
				"hi.lifetime_type,hi.enabled_lifetime,hi.enabled_lifetime_type"
			" from items ti"
			" left join items hi on hi.key_=ti.key_"
				" and hi.hostid=" ZBX_FS_UI64
			" where",
			hostid);
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "ti.hostid", templateids->values,
			templateids->values_num);

	result = zbx_db_select("%s", sql);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		item = (zbx_template_item_t *)zbx_malloc(NULL, sizeof(zbx_template_item_t));

		item->templateid_orig = 0;
		ZBX_STR2UINT64(item->templateid, row[0]);

		item->type_orig = 0;
		ZBX_STR2UCHAR(item->type, row[3]);

		item->value_type_orig = 0;
		ZBX_STR2UCHAR(item->value_type, row[4]);

		item->status_orig = 0;
		ZBX_STR2UCHAR(item->status, row[8]);

		item->valuemapid_orig = 0;
		ZBX_DBROW2UINT64(item->valuemapid, row[13]);

		item->authtype_orig = 0;
		ZBX_STR2UCHAR(item->authtype, row[17]);

		item->flags_orig = 0;
		ZBX_STR2UCHAR(item->flags, row[22]);

		item->inventory_link_orig = 0;
		ZBX_STR2UCHAR(item->inventory_link, row[24]);

		item->evaltype_orig = 0;
		ZBX_STR2UCHAR(item->evaltype, row[27]);

		item->interfaceid_orig = 0;
		switch (interface_type = zbx_get_interface_type_by_item_type(item->type))
		{
			case INTERFACE_TYPE_UNKNOWN:
			case INTERFACE_TYPE_OPT:
				item->interfaceid = 0;
				break;
			case INTERFACE_TYPE_ANY:
				for (i = 0; INTERFACE_TYPE_COUNT > i; i++)
				{
					if (0 != interfaceids[zbx_get_interface_type_priority(i) - 1])
						break;
				}
				item->interfaceid = interfaceids[zbx_get_interface_type_priority(i) - 1];
				break;
			default:
				item->interfaceid = interfaceids[interface_type - 1];
		}

		item->name_orig = NULL;
		item->name = zbx_strdup(NULL, row[1]);

		item->delay_orig = NULL;
		item->delay = zbx_strdup(NULL, row[5]);

		item->history_orig = NULL;
		item->history = zbx_strdup(NULL, row[6]);

		item->trends_orig = NULL;
		item->trends = zbx_strdup(NULL, row[7]);

		item->trapper_hosts_orig = NULL;
		item->trapper_hosts = zbx_strdup(NULL, row[9]);

		item->units_orig = NULL;
		item->units = zbx_strdup(NULL, row[10]);

		item->formula_orig = NULL;
		item->formula = zbx_strdup(NULL, row[11]);

		item->logtimefmt_orig = NULL;
		item->logtimefmt = zbx_strdup(NULL, row[12]);

		item->params_orig = NULL;
		item->params = zbx_strdup(NULL, row[14]);

		item->ipmi_sensor_orig = NULL;
		item->ipmi_sensor = zbx_strdup(NULL, row[15]);

		item->snmp_oid_orig = NULL;
		item->snmp_oid = zbx_strdup(NULL, row[16]);

		item->username_orig = NULL;
		item->username = zbx_strdup(NULL, row[18]);

		item->password_orig = NULL;
		item->password = zbx_strdup(NULL, row[19]);

		item->publickey_orig = NULL;
		item->publickey = zbx_strdup(NULL, row[20]);

		item->privatekey_orig = NULL;
		item->privatekey = zbx_strdup(NULL, row[21]);

		item->description_orig = NULL;
		item->description = zbx_strdup(NULL, row[23]);

		item->lifetime_orig = NULL;
		item->lifetime = zbx_strdup(NULL, row[25]);

		item->lifetime_type_orig = 0;
		ZBX_STR2UCHAR(item->lifetime_type, row[49]);

		item->enabled_lifetime_orig = NULL;
		item->enabled_lifetime = zbx_strdup(NULL, row[50]);

		item->enabled_lifetime_type_orig = 0;
		ZBX_STR2UCHAR(item->enabled_lifetime_type, row[51]);

		item->jmx_endpoint_orig = NULL;
		item->jmx_endpoint = zbx_strdup(NULL, row[28]);

		ZBX_DBROW2UINT64(item->master_itemid_orig, row[80]);
		ZBX_DBROW2UINT64(item->master_itemid, row[29]);

		item->timeout_orig = NULL;
		item->timeout = zbx_strdup(NULL, row[30]);

		item->url_orig = NULL;
		item->url = zbx_strdup(NULL, row[31]);

		item->query_fields_orig = NULL;
		item->query_fields = zbx_strdup(NULL, row[32]);

		item->posts_orig = NULL;
		item->posts = zbx_strdup(NULL, row[33]);

		item->status_codes_orig = NULL;
		item->status_codes = zbx_strdup(NULL, row[34]);

		item->follow_redirects_orig = 0;
		ZBX_STR2UCHAR(item->follow_redirects, row[35]);

		item->post_type_orig = 0;
		ZBX_STR2UCHAR(item->post_type, row[36]);

		item->http_proxy_orig = NULL;
		item->http_proxy = zbx_strdup(NULL, row[37]);

		item->headers_orig = NULL;
		item->headers = zbx_strdup(NULL, row[38]);

		item->retrieve_mode_orig = 0;
		ZBX_STR2UCHAR(item->retrieve_mode, row[39]);

		item->request_method_orig = 0;
		ZBX_STR2UCHAR(item->request_method, row[40]);

		item->output_format_orig = 0;
		ZBX_STR2UCHAR(item->output_format, row[41]);

		item->ssl_cert_file_orig = NULL;
		item->ssl_cert_file = zbx_strdup(NULL, row[42]);

		item->ssl_key_file_orig = NULL;
		item->ssl_key_file = zbx_strdup(NULL, row[43]);

		item->ssl_key_password_orig = NULL;
		item->ssl_key_password = zbx_strdup(NULL, row[44]);

		item->verify_peer_orig = 0;
		ZBX_STR2UCHAR(item->verify_peer, row[45]);

		item->verify_host_orig = 0;
		ZBX_STR2UCHAR(item->verify_host, row[46]);

		item->allow_traps_orig = 0;
		ZBX_STR2UCHAR(item->allow_traps, row[47]);

		item->discover_orig = 0;
		ZBX_STR2UCHAR(item->discover, row[48]);

		item->upd_flags = ZBX_FLAG_TEMPLATE_ITEM_UPDATE_RESET_FLAG;

		if (SUCCEED != zbx_db_is_null(row[26]))
		{
			unsigned char	uchar_orig;
			zbx_uint64_t	uint64_orig;

#define SET_FLAG_STR(r, i, f)			\
{						\
	if (0 != strcmp(r, (i)))		\
	{					\
		item->upd_flags |= f;		\
		i##_orig = zbx_strdup(NULL, r);	\
	}					\
}

#define SET_FLAG_UCHAR(r, i, f)		\
					\
{					\
	ZBX_STR2UCHAR(uchar_orig, (r));	\
	if (uchar_orig != (i))		\
	{				\
		item->upd_flags |= f;	\
		i##_orig = uchar_orig;	\
	}				\
}

#define SET_FLAG_UINT64(r, i, f)				\
	do							\
	{							\
		if (SUCCEED == zbx_db_is_null(r))		\
			uint64_orig = 0;			\
		else						\
			ZBX_STR2UINT64(uint64_orig, (r));	\
		if (uint64_orig != (i))				\
		{						\
			item->upd_flags |= f;			\
			i##_orig = uint64_orig;			\
		}						\
	}							\
	while(0)
			item->key = NULL;
			ZBX_STR2UINT64(item->itemid, row[26]);

			SET_FLAG_UINT64(row[52], item->interfaceid, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_INTERFACEID);
			SET_FLAG_UINT64(row[53], item->templateid, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_TEMPLATEID);
			SET_FLAG_STR(row[54], item->name, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_NAME);
			SET_FLAG_UCHAR(row[55], item->type, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_TYPE);
			SET_FLAG_UCHAR(row[56], item->value_type, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_VALUE_TYPE);
			SET_FLAG_STR(row[57], item->delay, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_DELAY);
			SET_FLAG_STR(row[58], item->history, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_HISTORY);
			SET_FLAG_STR(row[59], item->trends, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_TRENDS);
			SET_FLAG_UCHAR(row[60], item->status, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_STATUS);
			SET_FLAG_STR(row[61], item->trapper_hosts, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_TRAPPER_HOSTS);
			SET_FLAG_STR(row[62], item->units, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_UNITS);
			SET_FLAG_STR(row[63], item->formula, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_FORMULA);
			SET_FLAG_STR(row[64], item->logtimefmt, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_LOGTIMEFMT);
			SET_FLAG_UINT64(row[65], item->valuemapid, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_VALUEMAPID);
			SET_FLAG_STR(row[66], item->params, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_PARAMS);
			SET_FLAG_STR(row[67], item->ipmi_sensor, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_IPMI_SENSOR);
			SET_FLAG_STR(row[68], item->snmp_oid, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_SNMP_OID);
			SET_FLAG_UCHAR(row[69], item->authtype, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_AUTHTYPE);
			SET_FLAG_STR(row[70], item->username, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_USERNAME);
			SET_FLAG_STR(row[71], item->password, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_PASSWORD);
			SET_FLAG_STR(row[72], item->publickey, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_PUBLICKEY);
			SET_FLAG_STR(row[73], item->privatekey, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_PRIVATEKEY);
			SET_FLAG_UCHAR(row[74], item->flags, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_FLAGS);
			SET_FLAG_STR(row[75], item->description, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_DESCRIPTION);
			SET_FLAG_UCHAR(row[76], item->inventory_link, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_INVENTORY_LINK);
			SET_FLAG_STR(row[77], item->lifetime, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_LIFETIME);
			SET_FLAG_UCHAR(row[100], item->lifetime_type, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_LIFETIME_TYPE);
			SET_FLAG_STR(row[101], item->enabled_lifetime, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_ENABLED_LIFETIME);
			SET_FLAG_UCHAR(row[102], item->enabled_lifetime_type,
					ZBX_FLAG_TEMPLATE_ITEM_UPDATE_ENABLED_LIFETIME_TYPE);
			SET_FLAG_UCHAR(row[78], item->evaltype, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_EVALTYPE);
			SET_FLAG_STR(row[79], item->jmx_endpoint, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_JMX_ENDPOINT);
			SET_FLAG_STR(row[81], item->timeout, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_TIMEOUT);
			SET_FLAG_STR(row[82], item->url, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_URL);
			SET_FLAG_STR(row[83], item->query_fields, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_QUERY_FIELDS);
			SET_FLAG_STR(row[84], item->posts, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_POSTS);
			SET_FLAG_STR(row[85], item->status_codes, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_STATUS_CODES);
			SET_FLAG_UCHAR(row[86], item->follow_redirects, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_FOLLOW_REDIRECTS);
			SET_FLAG_UCHAR(row[87], item->post_type, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_POST_TYPE);
			SET_FLAG_STR(row[88], item->http_proxy, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_HTTP_PROXY);
			SET_FLAG_STR(row[89], item->headers, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_HEADERS);
			SET_FLAG_UCHAR(row[90], item->retrieve_mode, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_RETRIEVE_MODE);
			SET_FLAG_UCHAR(row[91], item->request_method, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_REQUEST_METHOD);
			SET_FLAG_UCHAR(row[92], item->output_format, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_OUTPUT_FORMAT);
			SET_FLAG_STR(row[93], item->ssl_cert_file, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_SSL_CERT_FILE);
			SET_FLAG_STR(row[94], item->ssl_key_file, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_SSL_KEY_FILE);
			SET_FLAG_STR(row[95], item->ssl_key_password, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_SSL_KEY_PASSWORD);
			SET_FLAG_UCHAR(row[96], item->verify_peer, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_VERIFY_PEER);
			SET_FLAG_UCHAR(row[97], item->verify_host, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_VERIFY_HOST);
			SET_FLAG_UCHAR(row[98], item->allow_traps, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_ALLOW_TRAPS);
			SET_FLAG_UCHAR(row[99], item->discover, ZBX_FLAG_TEMPLATE_ITEM_UPDATE_DISCOVER);
		}
		else
		{
			item->key = zbx_strdup(NULL, row[2]);
			item->itemid = 0;
		}

		zbx_vector_ptr_create(&item->dependent_items);
		zbx_vector_item_preproc_ptr_create(&item->item_preprocs);
		zbx_vector_item_preproc_ptr_create(&item->template_preprocs);
		zbx_vector_db_tag_ptr_create(&item->item_tags);
		zbx_vector_db_tag_ptr_create(&item->template_tags);
		zbx_vector_item_param_ptr_create(&item->item_params);
		zbx_vector_item_param_ptr_create(&item->template_params);
		zbx_vector_lld_macro_ptr_create(&item->item_lld_macros);
		zbx_vector_lld_macro_ptr_create(&item->template_lld_macros);
		zbx_vector_ptr_append(items, item);
	}
	zbx_db_free_result(result);

	zbx_free(sql);

	zbx_vector_ptr_sort(items, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
}

/******************************************************************************
 *                                                                            *
 * Purpose: reads template lld rule conditions and host lld_rule identifiers  *
 *          from database                                                     *
 *                                                                            *
 * Parameters: items - [IN] the host items including lld rules                *
 *             rules - [OUT] the lld rule mapping                             *
 *                                                                            *
 ******************************************************************************/
static void	get_template_lld_rule_map(const zbx_vector_ptr_t *items, zbx_vector_ptr_t *rules)
{
	zbx_template_item_t		*item;
	zbx_lld_rule_map_t		*rule;
	zbx_lld_rule_condition_t	*condition;
	int				i, index;
	zbx_vector_uint64_t		itemids;
	zbx_db_result_t			result;
	zbx_db_row_t			row;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			itemid, item_conditionid;

	zbx_vector_uint64_create(&itemids);

	/* prepare discovery rules */
	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		if (0 == (ZBX_FLAG_DISCOVERY_RULE & item->flags))
			continue;

		rule = (zbx_lld_rule_map_t *)zbx_malloc(NULL, sizeof(zbx_lld_rule_map_t));

		rule->itemid = item->itemid;
		rule->templateid = item->templateid;
		rule->conditionid = 0;
		zbx_vector_uint64_create(&rule->conditionids);
		zbx_vector_ptr_create(&rule->conditions);

		zbx_vector_ptr_append(rules, rule);

		if (0 != rule->itemid)
			zbx_vector_uint64_append(&itemids, rule->itemid);
		zbx_vector_uint64_append(&itemids, rule->templateid);
	}

	if (0 != itemids.values_num)
	{
		zbx_vector_ptr_sort(rules, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
		zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
				"select item_conditionid,itemid,operator,macro,value from item_condition where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids.values, itemids.values_num);

		result = zbx_db_select("%s", sql);

		while (NULL != (row = zbx_db_fetch(result)))
		{
			ZBX_STR2UINT64(itemid, row[1]);

			index = zbx_vector_ptr_bsearch(rules, &itemid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);

			if (FAIL != index)
			{
				/* read template lld conditions */

				rule = (zbx_lld_rule_map_t *)rules->values[index];

				condition = (zbx_lld_rule_condition_t *)zbx_malloc(NULL,
						sizeof(zbx_lld_rule_condition_t));

				ZBX_STR2UINT64(condition->item_conditionid, row[0]);
				ZBX_STR2UCHAR(condition->op, row[2]);
				condition->macro = zbx_strdup(NULL, row[3]);
				condition->value = zbx_strdup(NULL, row[4]);
				condition->upd_flags = ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_RESET_FLAG;
				condition->macro_orig = NULL;
				condition->value_orig = NULL;
				condition->op_orig = 0;

				zbx_vector_ptr_append(&rule->conditions, condition);
			}
			else
			{
				/* read host lld conditions identifiers */

				for (i = 0; i < rules->values_num; i++)
				{
					zbx_uint64_t	flags = ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_RESET_FLAG;

					rule = (zbx_lld_rule_map_t *)rules->values[i];

					if (itemid != rule->itemid)
						continue;

					index = rule->conditionids.values_num;

					if (rule->conditions.values_num > index)
					{
						unsigned char	uchar_orig;

						condition = (zbx_lld_rule_condition_t *)rule->conditions.values[index];
						ZBX_STR2UCHAR(uchar_orig, row[2]);

						if (uchar_orig != condition->op)
						{
							flags |= ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_OPERATOR;
							condition->op_orig = uchar_orig;
						}

						if (0 != strcmp(row[3], condition->macro))
						{
							flags |= ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_MACRO;
							condition->macro_orig = zbx_strdup(NULL, row[3]);
						}

						if (0 != strcmp(row[4], condition->value))
						{
							flags |= ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_VALUE;
							condition->value_orig = zbx_strdup(NULL, row[4]);
						}

						condition->upd_flags = flags;
					}

					ZBX_STR2UINT64(item_conditionid, row[0]);
					zbx_vector_uint64_append(&rule->conditionids, item_conditionid);

					break;
				}

				if (i == rules->values_num)
					THIS_SHOULD_NEVER_HAPPEN;
			}
		}
		zbx_db_free_result(result);

		zbx_free(sql);
	}

	zbx_vector_uint64_destroy(&itemids);
}

/******************************************************************************
 *                                                                            *
 * Purpose: calculate identifiers for new item conditions                     *
 *                                                                            *
 * Parameters: rules - [IN] the lld rule mapping                              *
 *                                                                            *
 * Return value: The number of new item conditions to be inserted.            *
 *                                                                            *
 ******************************************************************************/
static int	calculate_template_lld_rule_conditionids(zbx_vector_ptr_t *rules)
{
	zbx_lld_rule_map_t	*rule;
	int			i, conditions_num = 0;
	zbx_uint64_t		conditionid;

	/* calculate the number of new conditions to be inserted */
	for (i = 0; i < rules->values_num; i++)
	{
		rule = (zbx_lld_rule_map_t *)rules->values[i];

		if (rule->conditions.values_num > rule->conditionids.values_num)
			conditions_num += rule->conditions.values_num - rule->conditionids.values_num;
	}

	/* reserve ids for the new conditions to be inserted and assign to lld rules */
	if (0 == conditions_num)
		goto out;

	conditionid = zbx_db_get_maxid_num("item_condition", conditions_num);

	for (i = 0; i < rules->values_num; i++)
	{
		rule = (zbx_lld_rule_map_t *)rules->values[i];

		if (rule->conditions.values_num <= rule->conditionids.values_num)
			continue;

		rule->conditionid = conditionid;
		conditionid += rule->conditions.values_num - rule->conditionids.values_num;
	}
out:
	return conditions_num;
}

static void	update_template_lld_formula(char **formula, zbx_uint64_t id_proto, zbx_uint64_t id)
{
	char	srcid[64], dstid[64], *ptr;
	size_t	pos = 0, len;

	zbx_snprintf(srcid, sizeof(srcid), "{" ZBX_FS_UI64 "}", id_proto);
	zbx_snprintf(dstid, sizeof(dstid), "{" ZBX_FS_UI64 "}", id);

	len = strlen(srcid);

	while (NULL != (ptr = strstr(*formula + pos, srcid)))
	{
		pos = ptr - *formula + len - 1;
		zbx_replace_string(formula, ptr - *formula, &pos, dstid);
	}
}

/******************************************************************************
 *                                                                            *
 * Purpose: translate template item condition identifiers in expression type  *
 *          discovery rule formulas to refer the host item condition          *
 *          identifiers instead.                                              *
 *                                                                            *
 * Parameters:  items  - [IN] the template items                              *
 *              rules  - [IN] the lld rule mapping                            *
 *                                                                            *
 ******************************************************************************/
static void	update_template_lld_rule_formulas(zbx_vector_ptr_t *items, zbx_vector_ptr_t *rules)
{
	zbx_lld_rule_map_t	*rule;
	int			i, j, index;
	char			*formula;
	zbx_uint64_t		conditionid;

	for (i = 0; i < items->values_num; i++)
	{
		zbx_template_item_t	*item = (zbx_template_item_t *)items->values[i];

		if (0 == (ZBX_FLAG_DISCOVERY_RULE & item->flags) || ZBX_CONDITION_EVAL_TYPE_EXPRESSION !=
				item->evaltype)
		{
			continue;
		}

		index = zbx_vector_ptr_bsearch(rules, &item->templateid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);

		if (FAIL == index)
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		rule = (zbx_lld_rule_map_t *)rules->values[index];

		formula = zbx_strdup(NULL, item->formula);

		conditionid = rule->conditionid;

		for (j = 0; j < rule->conditions.values_num; j++)
		{
			zbx_uint64_t			id;
			zbx_lld_rule_condition_t	*condition =
					(zbx_lld_rule_condition_t *)rule->conditions.values[j];

			if (j < rule->conditionids.values_num)
				id = rule->conditionids.values[j];
			else
				id = conditionid++;

			update_template_lld_formula(&formula, condition->item_conditionid, id);
		}

		zbx_free(item->formula);
		item->formula = formula;
	}
}

/******************************************************************************
 *                                                                            *
 * Purpose: saves (inserts or updates) template item                          *
 *                                                                            *
 * Parameters: hostid             - [IN] parent host id                       *
 *             itemid             - [IN/OUT] item id used for insert          *
 *                                           operations                       *
 *             item               - [IN] item to be saved                     *
 *             db_insert_items    - [IN] prepared item bulk insert            *
 *             db_insert_irtdata  - [IN] prepared item discovery bulk insert  *
 *             audit_context_mode - [IN]                                      *
 *             sql                - [IN/OUT] sql buffer pointer used for      *
 *                                           update operations                *
 *             sql_alloc          - [IN/OUT] sql buffer already allocated     *
 *                                           memory                           *
 *             sql_offset         - [IN/OUT] offset for writing within sql    *
 *                                           buffer                           *
 *                                                                            *
 ******************************************************************************/
static void	save_template_item(zbx_uint64_t hostid, zbx_uint64_t *itemid, zbx_template_item_t *item,
		zbx_db_insert_t *db_insert_items, zbx_db_insert_t *db_insert_irtdata,
		zbx_db_insert_t *db_insert_irtname, int audit_context_mode, char **sql, size_t *sql_alloc,
		size_t *sql_offset)
{
	int			i;
	zbx_template_item_t	*dependent;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	if (NULL == item->key) /* existing item */
	{
		char		*str_esc;
		const char	*d = "";

		/* Even if there are no updates for an item, we must create audit entry for it */
		/* to accommodate other entities changes that depend on an item (like tags).   */
		zbx_audit_item_create_entry(audit_context_mode, ZBX_AUDIT_ACTION_UPDATE, item->itemid, item->name,
				item->flags);

		if (0 == item->upd_flags)
			goto dependent;

		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "update items set ");

#define PREPARE_UPDATE_ID(FLAG_POSTFIX, field)									\
		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))			\
		{												\
			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s"#field"=%s", d,			\
					zbx_db_sql_id_ins(item->field));					\
			d = ",";										\
														\
			zbx_audit_item_update_json_update_##field(audit_context_mode, item->itemid,		\
					item->flags, item->field##_orig, item->field);				\
		}												\

#define PREPARE_UPDATE_STR(FLAG_POSTFIX, field)									\
		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))			\
		{												\
			str_esc = zbx_db_dyn_escape_string(item->field);					\
			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s"#field"='%s'", d, str_esc);		\
			d = ",";										\
			zbx_free(str_esc);									\
														\
			zbx_audit_item_update_json_update_##field(audit_context_mode, item->itemid,		\
					item->flags, item->field##_orig, item->field);				\
		}												\

#define PREPARE_UPDATE_STR_SECRET(FLAG_POSTFIX, field)								\
		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))			\
		{												\
			str_esc = zbx_db_dyn_escape_string(item->field);					\
			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s"#field"='%s'", d, str_esc);		\
			d = ",";										\
			zbx_free(str_esc);									\
														\
			zbx_audit_item_update_json_update_##field(audit_context_mode, item->itemid,		\
					item->flags, (0 == strcmp("", item->field##_orig) ? "" :		\
					ZBX_MACRO_SECRET_MASK), (0 == strcmp("", item->field) ? "" :		\
					ZBX_MACRO_SECRET_MASK));						\
		}												\

#define PREPARE_UPDATE_UC(FLAG_POSTFIX, field)				\
		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))			\
		{												\
			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s"#field"=%d", d, (int)item->field);	\
			d = ",";										\
														\
			zbx_audit_item_update_json_update_##field(audit_context_mode, item->itemid,		\
					item->flags, (int)item->field##_orig, (int)item->field);		\
		}												\

#define PREPARE_UPDATE_UINT64(FLAG_POSTFIX, field)			\
		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))			\
		{												\
			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s"#field"=" ZBX_FS_UI64, d,		\
					item->field);								\
			d = ",";										\
														\
			zbx_audit_item_update_json_update_##field(audit_context_mode, item->itemid,		\
					item->flags, item->field##_orig, item->field);				\
		}

		PREPARE_UPDATE_ID(INTERFACEID, interfaceid)
		PREPARE_UPDATE_STR(NAME, name)
		PREPARE_UPDATE_UC(TYPE, type)
		PREPARE_UPDATE_UINT64(TEMPLATEID, templateid)
		PREPARE_UPDATE_UC(VALUE_TYPE, value_type)
		PREPARE_UPDATE_STR(DELAY, delay);
		PREPARE_UPDATE_STR(HISTORY, history)
		PREPARE_UPDATE_STR(TRENDS, trends)
		PREPARE_UPDATE_UC(STATUS, status)
		PREPARE_UPDATE_STR(TRAPPER_HOSTS, trapper_hosts)
		PREPARE_UPDATE_STR(UNITS, units)
		PREPARE_UPDATE_STR(FORMULA, formula)
		PREPARE_UPDATE_STR(LOGTIMEFMT, logtimefmt)
		PREPARE_UPDATE_ID(VALUEMAPID, valuemapid)
		PREPARE_UPDATE_STR(PARAMS, params)
		PREPARE_UPDATE_STR(IPMI_SENSOR, ipmi_sensor)
		PREPARE_UPDATE_STR(SNMP_OID, snmp_oid)
		PREPARE_UPDATE_UC(AUTHTYPE, authtype)
		PREPARE_UPDATE_STR(USERNAME, username)
		PREPARE_UPDATE_STR_SECRET(PASSWORD, password)
		PREPARE_UPDATE_STR(PUBLICKEY, publickey)
		PREPARE_UPDATE_STR(PRIVATEKEY, privatekey)
		PREPARE_UPDATE_UC(FLAGS, flags)
		PREPARE_UPDATE_STR(DESCRIPTION, description)
		PREPARE_UPDATE_UC(INVENTORY_LINK, inventory_link)
		PREPARE_UPDATE_STR(LIFETIME, lifetime)
		PREPARE_UPDATE_UC(LIFETIME_TYPE, lifetime_type)
		PREPARE_UPDATE_STR(ENABLED_LIFETIME, enabled_lifetime)
		PREPARE_UPDATE_UC(ENABLED_LIFETIME_TYPE, enabled_lifetime_type)
		PREPARE_UPDATE_UC(EVALTYPE, evaltype)
		PREPARE_UPDATE_STR(JMX_ENDPOINT, jmx_endpoint)
		PREPARE_UPDATE_ID(MASTER_ITEMID, master_itemid)
		PREPARE_UPDATE_STR(TIMEOUT, timeout)
		PREPARE_UPDATE_STR(URL, url)
		PREPARE_UPDATE_STR(QUERY_FIELDS, query_fields)
		PREPARE_UPDATE_STR(POSTS, posts)
		PREPARE_UPDATE_STR(STATUS_CODES, status_codes)
		PREPARE_UPDATE_UC(FOLLOW_REDIRECTS, follow_redirects)
		PREPARE_UPDATE_UC(POST_TYPE, post_type)
		PREPARE_UPDATE_STR(HTTP_PROXY, http_proxy)
		PREPARE_UPDATE_STR(HEADERS, headers)
		PREPARE_UPDATE_UC(RETRIEVE_MODE, retrieve_mode)
		PREPARE_UPDATE_UC(REQUEST_METHOD, request_method)
		PREPARE_UPDATE_UC(OUTPUT_FORMAT, output_format)
		PREPARE_UPDATE_STR(SSL_CERT_FILE, ssl_cert_file)
		PREPARE_UPDATE_STR(SSL_KEY_FILE, ssl_key_file)
		PREPARE_UPDATE_STR_SECRET(SSL_KEY_PASSWORD, ssl_key_password)
		PREPARE_UPDATE_UC(VERIFY_PEER, verify_peer)
		PREPARE_UPDATE_UC(VERIFY_HOST, verify_host)
		PREPARE_UPDATE_UC(ALLOW_TRAPS, allow_traps)
		PREPARE_UPDATE_UC(DISCOVER, discover)
		ZBX_UNUSED(d);

		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, " where itemid=" ZBX_FS_UI64 ";\n", item->itemid);

		if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_NAME))
		{
			if (ZBX_FLAG_DISCOVERY_NORMAL == item->flags || ZBX_FLAG_DISCOVERY_CREATED == item->flags)
			{
				str_esc = zbx_db_dyn_escape_string(item->name);

				zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "update item_rtname set"
						" name_resolved='%s',name_resolved_upper=upper('%s')"
						" where itemid=" ZBX_FS_UI64 ";\n",
						str_esc, str_esc, item->itemid);
				zbx_free(str_esc);
				zbx_db_execute_overflowed_sql(sql, sql_alloc, sql_offset);
			}
		}
	}
	else
	{
		zbx_db_insert_add_values(db_insert_items, *itemid, item->name, item->key, hostid, (int)item->type,
				(int)item->value_type, item->delay, item->history, item->trends,
				(int)item->status, item->trapper_hosts, item->units, item->formula, item->logtimefmt,
				item->valuemapid, item->params, item->ipmi_sensor, item->snmp_oid, (int)item->authtype,
				item->username, item->password, item->publickey, item->privatekey, item->templateid,
				(int)item->flags, item->description, (int)item->inventory_link, item->interfaceid,
				item->lifetime, (int)item->lifetime_type, item->enabled_lifetime,
				(int)item->enabled_lifetime_type, (int)item->evaltype,
				item->jmx_endpoint, item->master_itemid, item->timeout, item->url, item->query_fields,
				item->posts, item->status_codes, item->follow_redirects, item->post_type,
				item->http_proxy, item->headers, item->retrieve_mode, item->request_method,
				item->output_format, item->ssl_cert_file, item->ssl_key_file, item->ssl_key_password,
				item->verify_peer, item->verify_host, item->allow_traps, item->discover);

		zbx_db_insert_add_values(db_insert_irtdata, *itemid);

		if (ZBX_FLAG_DISCOVERY_NORMAL == item->flags || ZBX_FLAG_DISCOVERY_CREATED == item->flags)
			zbx_db_insert_add_values(db_insert_irtname, *itemid, item->name, item->name);

		zbx_audit_item_create_entry(audit_context_mode, ZBX_AUDIT_ACTION_ADD, *itemid, item->name, item->flags);
		zbx_audit_item_update_json_add_data(audit_context_mode, *itemid, item, hostid);

		item->itemid = (*itemid)++;
	}
dependent:
	for (i = 0; i < item->dependent_items.values_num; i++)
	{
		dependent = (zbx_template_item_t *)item->dependent_items.values[i];

		if (dependent->master_itemid_orig != item->itemid)
			dependent->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_UPDATE_MASTER_ITEMID;

		dependent->master_itemid = item->itemid;
		save_template_item(hostid, itemid, dependent, db_insert_items, db_insert_irtdata, db_insert_irtname,
				audit_context_mode, sql, sql_alloc, sql_offset);
	}

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: saves template items to target host in database                   *
 *                                                                            *
 * Parameters:                                                                *
 *              hostid             - [IN] target host                         *
 *              items              - [IN] template items                      *
 *              audit_context_mode - [IN]                                     *
 *                                                                            *
 ******************************************************************************/
static void	save_template_items(zbx_uint64_t hostid, zbx_vector_ptr_t *items, int audit_context_mode)
{
	char			*sql = NULL;
	size_t			sql_alloc = 16 * ZBX_KIBIBYTE, sql_offset = 0;
	int			new_items = 0, upd_items = 0, i;
	zbx_uint64_t		itemid = 0;
	zbx_db_insert_t		db_insert_items, db_insert_irtdata, db_insert_irtname;
	zbx_template_item_t	*item;

	if (0 == items->values_num)
		return;

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		if (NULL == item->key)
			upd_items++;
		else
			new_items++;
	}

	if (0 != new_items)
	{
		itemid = zbx_db_get_maxid_num("items", new_items);

		zbx_db_insert_prepare(&db_insert_items, "items", "itemid", "name", "key_", "hostid", "type",
				"value_type", "delay", "history", "trends", "status", "trapper_hosts", "units",
				"formula", "logtimefmt", "valuemapid", "params", "ipmi_sensor",
				"snmp_oid", "authtype", "username", "password", "publickey", "privatekey",
				"templateid", "flags", "description", "inventory_link", "interfaceid", "lifetime",
				"lifetime_type", "enabled_lifetime", "enabled_lifetime_type", "evaltype","jmx_endpoint",
				"master_itemid", "timeout", "url", "query_fields", "posts", "status_codes",
				"follow_redirects", "post_type", "http_proxy", "headers", "retrieve_mode",
				"request_method", "output_format", "ssl_cert_file", "ssl_key_file", "ssl_key_password",
				"verify_peer", "verify_host", "allow_traps", "discover", (char *)NULL);

		zbx_db_insert_prepare(&db_insert_irtdata, "item_rtdata", "itemid", (char *)NULL);
		zbx_db_insert_prepare(&db_insert_irtname, "item_rtname", "itemid", "name_resolved",
				"name_resolved_upper", (char *)NULL);
	}

	if (0 != upd_items)
	{
		sql = (char *)zbx_malloc(sql, sql_alloc);
		zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);
	}

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		/* dependent items are saved within recursive save_template_item calls while saving master */
		if (0 == item->master_itemid)
		{
			save_template_item(hostid, &itemid, item, &db_insert_items, &db_insert_irtdata,
					&db_insert_irtname, audit_context_mode, &sql, &sql_alloc, &sql_offset);
		}
	}

	if (0 != new_items)
	{
		zbx_db_insert_execute(&db_insert_items);
		zbx_db_insert_clean(&db_insert_items);

		zbx_db_insert_execute(&db_insert_irtname);
		zbx_db_insert_clean(&db_insert_irtname);
		zbx_db_insert_execute(&db_insert_irtdata);
		zbx_db_insert_clean(&db_insert_irtdata);
	}

	if (0 != upd_items)
	{
		zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

		if (16 < sql_offset)
			zbx_db_execute("%s", sql);

		zbx_free(sql);
	}

	zbx_vector_ptr_sort(items, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
}

/******************************************************************************
 *                                                                            *
 * Purpose: saves template lld rule item conditions to the target host in     *
 *          database                                                          *
 *                                                                            *
 * Parameters:  items              - [IN] template items                      *
 *              rules              - [IN] lld rule mapping                    *
 *              new_conditions     - [IN] number of new item conditions to be *
 *                                        inserted                            *
 *              audit_context_mode - [IN]                                     *
 *                                                                            *
 ******************************************************************************/
static void	save_template_lld_rules(zbx_vector_ptr_t *items, zbx_vector_ptr_t *rules, int new_conditions,
		int audit_context_mode)
{
	char				*macro_esc, *value_esc;
	int				i, j, index;
	zbx_db_insert_t			db_insert;
	zbx_lld_rule_map_t		*rule;
	zbx_lld_rule_condition_t	*condition;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_vector_uint64_t		item_conditionids;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	if (0 == rules->values_num)
		return;

	zbx_vector_uint64_create(&item_conditionids);

	if (0 != new_conditions)
	{
		zbx_db_insert_prepare(&db_insert, "item_condition", "item_conditionid", "itemid", "operator", "macro",
				"value", (char *)NULL);

		/* insert lld rule conditions for new items */
		for (i = 0; i < items->values_num; i++)
		{
			zbx_template_item_t	*item = (zbx_template_item_t *)items->values[i];

			if (NULL == item->key)
				continue;

			if (0 == (ZBX_FLAG_DISCOVERY_RULE & item->flags))
				continue;

			index = zbx_vector_ptr_bsearch(rules, &item->templateid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);

			if (FAIL == index)
			{
				THIS_SHOULD_NEVER_HAPPEN;
				continue;
			}

			rule = (zbx_lld_rule_map_t *)rules->values[index];

			for (j = 0; j < rule->conditions.values_num; j++)
			{
				condition = (zbx_lld_rule_condition_t *)rule->conditions.values[j];

				zbx_db_insert_add_values(&db_insert, rule->conditionid, item->itemid,
						(int)condition->op, condition->macro, condition->value);

				zbx_audit_discovery_rule_update_json_add_filter_conditions(audit_context_mode,
						item->itemid, rule->conditionid, condition->op, condition->macro,
						condition->value);

				rule->conditionid++;
			}
		}
	}

	zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

	/* update lld rule conditions for existing items */
	for (i = 0; i < rules->values_num; i++)
	{
		rule = (zbx_lld_rule_map_t *)rules->values[i];

		/* skip lld rules of new items */
		if (0 == rule->itemid)
			continue;

		index = MIN(rule->conditions.values_num, rule->conditionids.values_num);

		/* update intersecting rule conditions */
		for (j = 0; j < index; j++)
		{
			const char	*d = "";

			condition = (zbx_lld_rule_condition_t *)rule->conditions.values[j];

			if (0 == condition->upd_flags)
				continue;

			zbx_audit_discovery_rule_update_json_update_filter_conditions_create_entry(audit_context_mode,
					rule->itemid, rule->conditionids.values[j]);

			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update item_condition set ");
			if (0 != (condition->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_OPERATOR))
			{
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%soperator=%d", d,
						(int)condition->op);
				d = ",";
				zbx_audit_discovery_rule_update_json_update_filter_conditions_operator(audit_context_mode,
						rule->itemid, rule->conditionids.values[j], (int)condition->op_orig,
						(int)condition->op);
			}
			if (0 != (condition->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_MACRO))
			{
				macro_esc = zbx_db_dyn_escape_string(condition->macro);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%smacro='%s'", d, macro_esc);
				d = ",";
				zbx_free(macro_esc);

				zbx_audit_discovery_rule_update_json_update_filter_conditions_macro(audit_context_mode,
						rule->itemid, rule->conditionids.values[j], condition->macro_orig,
						condition->macro);
			}
			if (0 != (condition->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_CONDITION_UPDATE_VALUE))
			{
				value_esc = zbx_db_dyn_escape_string(condition->value);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%svalue='%s'", d, value_esc);
				zbx_free(value_esc);

				zbx_audit_discovery_rule_update_json_update_filter_conditions_value(audit_context_mode,
						rule->itemid, rule->conditionids.values[j], condition->value_orig,
						condition->value);
			}
			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where item_conditionid=" ZBX_FS_UI64 ";\n",
					rule->conditionids.values[j]);

			zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}

		/* delete removed rule conditions */
		for (j = index; j < rule->conditionids.values_num; j++)
		{
			zbx_audit_discovery_rule_update_json_delete_filter_conditions(audit_context_mode, rule->itemid,
					rule->conditionids.values[j]);
			zbx_vector_uint64_append(&item_conditionids, rule->conditionids.values[j]);
		}

		/* insert new rule conditions */
		for (j = index; j < rule->conditions.values_num; j++)
		{
			condition = (zbx_lld_rule_condition_t *)rule->conditions.values[j];

			zbx_db_insert_add_values(&db_insert, rule->conditionid, rule->itemid,
					(int)condition->op, condition->macro, condition->value);

			zbx_audit_discovery_rule_update_json_add_filter_conditions(audit_context_mode, rule->itemid,
					rule->conditionid, condition->op, condition->macro, condition->value);

			rule->conditionid++;
		}
	}

	/* delete removed item conditions */
	if (0 != item_conditionids.values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_condition where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "item_conditionid", item_conditionids.values,
				item_conditionids.values_num);
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
	}

	zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

	if (16 < sql_offset)
		zbx_db_execute("%s", sql);

	if (0 != new_conditions)
	{
		zbx_db_insert_execute(&db_insert);
		zbx_db_insert_clean(&db_insert);
	}

	zbx_free(sql);
	zbx_vector_uint64_destroy(&item_conditionids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: saves host item prototypes in database                            *
 *                                                                            *
 * Parameters:  hostid  - [IN] target host                                    *
 *              items   - [IN] template items                                 *
 *                                                                            *
 ******************************************************************************/
static void	save_template_discovery_prototypes(zbx_uint64_t hostid, zbx_vector_ptr_t *items)
{
	typedef struct
	{
		zbx_uint64_t	itemid;
		zbx_uint64_t	parent_itemid;
	}
	zbx_proto_t;

	zbx_db_result_t		result;
	zbx_db_row_t		row;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_vector_uint64_t	itemids;
	zbx_vector_ptr_t	prototypes;
	zbx_proto_t		*proto;
	int			i;
	zbx_db_insert_t		db_insert;

	zbx_vector_ptr_create(&prototypes);
	zbx_vector_uint64_create(&itemids);

	for (i = 0; i < items->values_num; i++)
	{
		zbx_template_item_t	*item = (zbx_template_item_t *)items->values[i];

		/* process only new prototype items */
		if (NULL == item->key || 0 == (ZBX_FLAG_DISCOVERY_PROTOTYPE & item->flags))
			continue;

		zbx_vector_uint64_append(&itemids, item->itemid);
	}

	if (0 == itemids.values_num)
		goto out;

	zbx_vector_uint64_sort(&itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
			"select i.itemid,r.itemid"
			" from items i,item_discovery id,items r"
			" where i.templateid=id.itemid"
				" and id.parent_itemid=r.templateid"
				" and r.hostid=" ZBX_FS_UI64
				" and",
			hostid);
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.itemid", itemids.values, itemids.values_num);

	result = zbx_db_select("%s", sql);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		proto = (zbx_proto_t *)zbx_malloc(NULL, sizeof(zbx_proto_t));

		ZBX_STR2UINT64(proto->itemid, row[0]);
		ZBX_STR2UINT64(proto->parent_itemid, row[1]);

		zbx_vector_ptr_append(&prototypes, proto);
	}
	zbx_db_free_result(result);

	if (0 == prototypes.values_num)
		goto out;

	zbx_db_insert_prepare(&db_insert, "item_discovery", "itemdiscoveryid", "itemid",
					"parent_itemid", (char *)NULL);

	for (i = 0; i < prototypes.values_num; i++)
	{
		proto = (zbx_proto_t *)prototypes.values[i];

		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), proto->itemid, proto->parent_itemid);
	}

	zbx_db_insert_autoincrement(&db_insert, "itemdiscoveryid");
	zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);
out:
	zbx_free(sql);

	zbx_vector_uint64_destroy(&itemids);

	zbx_vector_ptr_clear_ext(&prototypes, zbx_ptr_free);
	zbx_vector_ptr_destroy(&prototypes);
}

static zbx_template_item_preproc_t	*zbx_item_preproc_create(const char *item_preprocid, int step, int type,
		const char *params, int error_handler, const char *error_handler_params)
{
	zbx_template_item_preproc_t	*preproc;

	preproc = (zbx_template_item_preproc_t *)zbx_malloc(NULL, sizeof(zbx_template_item_preproc_t));

	preproc->upd_flags = ZBX_FLAG_TEMPLATE_ITEM_PREPROC_RESET_FLAG;
	ZBX_STR2UINT64(preproc->item_preprocid, item_preprocid);
	preproc->step = step;
	preproc->type = type;
	preproc->params = zbx_strdup(NULL, params);
	preproc->error_handler = error_handler;
	preproc->error_handler_params = zbx_strdup(NULL, error_handler_params);
	preproc->type_orig = 0;
	preproc->params_orig = NULL;
	preproc->error_handler_orig = 0;
	preproc->error_handler_params_orig = NULL;

	return preproc;
}

static void	zbx_item_preproc_free(zbx_template_item_preproc_t *preproc)
{
	if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_PARAMS))
		zbx_free(preproc->params_orig);
	zbx_free(preproc->params);

	zbx_free(preproc->error_handler_params);
	if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER_PARAMS))
		zbx_free(preproc->error_handler_params_orig);

	zbx_free(preproc);
}

static void	zbx_lld_macros_free(zbx_template_lld_macro_t *macro)
{
	if (0 != (macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_LLD_MACRO))
		zbx_free(macro->lld_macro_orig);
	zbx_free(macro->lld_macro);

	if (0 != (macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_PATH))
		zbx_free(macro->path_orig);
	zbx_free(macro->path);

	zbx_free(macro);
}

/******************************************************************************
 *                                                                            *
 * Purpose: frees template item                                               *
 *                                                                            *
 * Parameters:  item  - [IN] the template item                                *
 *                                                                            *
 ******************************************************************************/
static void	free_template_item(zbx_template_item_t *item)
{
	zbx_vector_ptr_destroy(&item->dependent_items);
	zbx_vector_item_preproc_ptr_clear_ext(&item->item_preprocs, zbx_item_preproc_free);
	zbx_vector_item_preproc_ptr_clear_ext(&item->template_preprocs, zbx_item_preproc_free);
	zbx_vector_db_tag_ptr_clear_ext(&item->item_tags, zbx_db_tag_free);
	zbx_vector_db_tag_ptr_clear_ext(&item->template_tags, zbx_db_tag_free);
	zbx_vector_item_param_ptr_clear_ext(&item->item_params, zbx_item_param_free);
	zbx_vector_item_param_ptr_clear_ext(&item->template_params, zbx_item_param_free);
	zbx_vector_lld_macro_ptr_clear_ext(&item->item_lld_macros, zbx_lld_macros_free);
	zbx_vector_lld_macro_ptr_clear_ext(&item->template_lld_macros, zbx_lld_macros_free);
	zbx_vector_item_preproc_ptr_destroy(&item->item_preprocs);
	zbx_vector_item_preproc_ptr_destroy(&item->template_preprocs);
	zbx_vector_db_tag_ptr_destroy(&item->item_tags);
	zbx_vector_db_tag_ptr_destroy(&item->template_tags);
	zbx_vector_item_param_ptr_destroy(&item->item_params);
	zbx_vector_item_param_ptr_destroy(&item->template_params);
	zbx_vector_lld_macro_ptr_destroy(&item->item_lld_macros);
	zbx_vector_lld_macro_ptr_destroy(&item->template_lld_macros);

#define CLEAN_ORIG(FLAG_POSTFIX, field)							\
	if (0 != (item->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_UPDATE_##FLAG_POSTFIX))	\
	{										\
		zbx_free(item->field##_orig);						\
	}										\
	zbx_free(item->field);

	CLEAN_ORIG(NAME, name)
	CLEAN_ORIG(DELAY, delay)
	CLEAN_ORIG(HISTORY, history)
	CLEAN_ORIG(TRENDS, trends)
	CLEAN_ORIG(TRAPPER_HOSTS, trapper_hosts)
	CLEAN_ORIG(UNITS, units)
	CLEAN_ORIG(FORMULA, formula)
	CLEAN_ORIG(LOGTIMEFMT, logtimefmt)
	CLEAN_ORIG(PARAMS, params)
	CLEAN_ORIG(IPMI_SENSOR, ipmi_sensor)
	CLEAN_ORIG(SNMP_OID, snmp_oid)
	CLEAN_ORIG(USERNAME, username)
	CLEAN_ORIG(PASSWORD, password)
	CLEAN_ORIG(PUBLICKEY, publickey)
	CLEAN_ORIG(PRIVATEKEY, privatekey)
	CLEAN_ORIG(DESCRIPTION, description)
	CLEAN_ORIG(LIFETIME, lifetime)
	CLEAN_ORIG(ENABLED_LIFETIME, enabled_lifetime)
	CLEAN_ORIG(JMX_ENDPOINT, jmx_endpoint)
	CLEAN_ORIG(TIMEOUT, timeout)
	CLEAN_ORIG(URL, url)
	CLEAN_ORIG(QUERY_FIELDS, query_fields)
	CLEAN_ORIG(POSTS, posts)
	CLEAN_ORIG(STATUS_CODES, status_codes)
	CLEAN_ORIG(HTTP_PROXY, http_proxy)
	CLEAN_ORIG(HEADERS, headers)
	CLEAN_ORIG(SSL_CERT_FILE, ssl_cert_file)
	CLEAN_ORIG(SSL_KEY_FILE, ssl_key_file)
	CLEAN_ORIG(SSL_KEY_PASSWORD, ssl_key_password)
#undef CLEAN_ORIG
	zbx_free(item->key);

	zbx_free(item);
}

/******************************************************************************
 *                                                                            *
 * Purpose: frees lld rule condition                                          *
 *                                                                            *
 * Parameters:  condition  - [IN]                                             *
 *                                                                            *
 ******************************************************************************/
static void	free_lld_rule_condition(zbx_lld_rule_condition_t *condition)
{
	/* cannot use update flags to check if orig values were set, because they get reset */
	zbx_free(condition->macro_orig);
	zbx_free(condition->value_orig);

	zbx_free(condition->macro);
	zbx_free(condition->value);
	zbx_free(condition);
}

/******************************************************************************
 *                                                                            *
 * Purpose: frees lld rule mapping                                            *
 *                                                                            *
 * Parameters:  item  - [IN] the lld rule mapping                             *
 *                                                                            *
 ******************************************************************************/
static void	free_lld_rule_map(zbx_lld_rule_map_t *rule)
{
	zbx_vector_ptr_clear_ext(&rule->conditions, (zbx_clean_func_t)free_lld_rule_condition);
	zbx_vector_ptr_destroy(&rule->conditions);

	zbx_vector_uint64_destroy(&rule->conditionids);

	zbx_free(rule);
}

static zbx_hash_t	template_item_hash_func(const void *d)
{
	const zbx_template_item_t	*item = *(const zbx_template_item_t * const *)d;

	return ZBX_DEFAULT_UINT64_HASH_FUNC(&item->templateid);
}

static int	template_item_compare_func(const void *d1, const void *d2)
{
	const zbx_template_item_t	*item1 = *(const zbx_template_item_t * const *)d1;
	const zbx_template_item_t	*item2 = *(const zbx_template_item_t * const *)d2;

	ZBX_RETURN_IF_NOT_EQUAL(item1->templateid, item2->templateid);
	return 0;
}

/******************************************************************************
 *                                                                            *
 * Purpose: copy template item preprocessing options                          *
 *                                                                            *
 * Parameters: items       - [IN] vector of new/updated items                 *
 *                                                                            *
 ******************************************************************************/
static void	copy_template_items_preproc(const zbx_vector_ptr_t *items, int audit_context_mode)
{
	int				i, j, new_preproc_num = 0, update_preproc_num = 0, delete_preproc_num = 0;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_template_item_t		*item;
	zbx_template_item_preproc_t	*preproc;
	zbx_vector_uint64_t		deleteids;
	zbx_db_insert_t			db_insert;
	zbx_uint64_t			new_preprocid = 0;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&deleteids);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_preprocs.values_num; j++)
		{
			preproc = (zbx_template_item_preproc_t *)item->item_preprocs.values[j];

			if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_DELETE))
			{
				zbx_vector_uint64_append(&deleteids, preproc->item_preprocid);
				zbx_audit_item_delete_preproc(audit_context_mode, item->itemid, item->flags,
						preproc->item_preprocid);
				continue;
			}

			if (0 == preproc->item_preprocid)
			{
				new_preproc_num++;
				continue;
			}

			if (0 == (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE))
				continue;

			update_preproc_num++;
		}
	}

	if (0 != update_preproc_num)
		zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

	if (0 != new_preproc_num)
	{
		new_preprocid = zbx_db_get_maxid_num("item_preproc", new_preproc_num);

		zbx_db_insert_prepare(&db_insert, "item_preproc", "item_preprocid", "itemid", "step", "type", "params",
				"error_handler", "error_handler_params", (char *)NULL);
	}

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_preprocs.values_num; j++)
		{
			const char	*d = "";

			preproc = (zbx_template_item_preproc_t *)item->item_preprocs.values[j];
			if (0 == preproc->item_preprocid)
			{
				zbx_db_insert_add_values(&db_insert, new_preprocid, item->itemid, preproc->step,
						preproc->type, preproc->params, preproc->error_handler,
						preproc->error_handler_params);

				zbx_audit_item_update_json_add_item_preproc(audit_context_mode, item->itemid, new_preprocid,
						item->flags, preproc->step, preproc->type, preproc->params,
						preproc->error_handler, preproc->error_handler_params);

				new_preprocid++;

				continue;
			}

			if (0 == (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE))
				continue;

			zbx_audit_item_update_json_update_item_preproc_create_entry(audit_context_mode, item->itemid,
					item->flags, preproc->item_preprocid);

			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update item_preproc set ");

			if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_TYPE))
			{
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%stype=%d", d, preproc->type);
				d = ",";

				zbx_audit_item_update_json_update_item_preproc_type(audit_context_mode, item->itemid,
						item->flags, preproc->item_preprocid, preproc->type_orig,
						preproc->type);
			}

			if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_PARAMS))
			{
				char	*params_esc;

				params_esc = zbx_db_dyn_escape_string(preproc->params);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%sparams='%s'", d, params_esc);

				zbx_free(params_esc);
				d = ",";

				zbx_audit_item_update_json_update_item_preproc_params(audit_context_mode, item->itemid,
						item->flags, preproc->item_preprocid, preproc->params_orig,
						preproc->params);
			}

			if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER))
			{
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%serror_handler=%d", d,
						preproc->error_handler);
				d = ",";

				zbx_audit_item_update_json_update_item_preproc_error_handler(audit_context_mode,
						item->itemid, item->flags, preproc->item_preprocid,
						preproc->error_handler_orig, preproc->error_handler);
			}

			if (0 != (preproc->upd_flags & ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER_PARAMS))
			{
				char	*params_esc;

				params_esc = zbx_db_dyn_escape_string(preproc->error_handler_params);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%serror_handler_params='%s'", d,
						params_esc);

				zbx_free(params_esc);

				zbx_audit_item_update_json_update_item_preproc_error_handler_params(audit_context_mode,
						item->itemid, item->flags, preproc->item_preprocid,
						preproc->error_handler_params_orig, preproc->error_handler_params);
			}

			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where item_preprocid=" ZBX_FS_UI64 ";\n",
					preproc->item_preprocid);

			zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}
	}

	if (0 != update_preproc_num)
	{
		zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

		if (16 < sql_offset)	/* in ORACLE always present begin..end; */
			zbx_db_execute("%s", sql);
	}

	if (0 != new_preproc_num)
	{
		zbx_db_insert_execute(&db_insert);
		zbx_db_insert_clean(&db_insert);
	}

	if (0 != deleteids.values_num)
	{
		sql_offset = 0;
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_preproc where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "item_preprocid", deleteids.values,
				deleteids.values_num);
		zbx_db_execute("%s", sql);

		delete_preproc_num = deleteids.values_num;
	}
	zbx_free(sql);
	zbx_vector_uint64_destroy(&deleteids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() added:%d updated:%d removed:%d", __func__, new_preproc_num,
			update_preproc_num, delete_preproc_num);
}

/******************************************************************************
 *                                                                            *
 * Purpose: copies template item tags                                         *
 *                                                                            *
 * Parameters:                                                                *
 *             items              - [IN] vector of new/updated items          *
 *             audit_context_mode - [IN]                                      *
 *                                                                            *
 ******************************************************************************/
static void	copy_template_item_tags(const zbx_vector_ptr_t *items, int audit_context_mode)
{
	int				i, j, new_tag_num = 0, update_tag_num = 0, delete_tag_num = 0;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_template_item_t		*item;
	zbx_db_tag_t			*tag;
	zbx_vector_uint64_t		deleteids;
	zbx_db_insert_t			db_insert;
	zbx_uint64_t			new_tagid = 0;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&deleteids);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_tags.values_num; j++)
		{
			tag = item->item_tags.values[j];

			if (0 != (tag->flags & ZBX_FLAG_DB_TAG_REMOVE))
			{
				zbx_vector_uint64_append(&deleteids, tag->tagid);
				zbx_audit_item_delete_tag(audit_context_mode, item->itemid, item->flags, tag->tagid);
				continue;
			}

			if (0 == tag->tagid)
			{
				new_tag_num++;
				continue;
			}

			if (0 == (tag->flags & ZBX_FLAG_DB_TAG_UPDATE))
				continue;

			update_tag_num++;
		}
	}

	if (0 != update_tag_num)
		zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

	if (0 != new_tag_num)
	{
		new_tagid = zbx_db_get_maxid_num("item_tag", new_tag_num);
		zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", (char *)NULL);
	}

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_tags.values_num; j++)
		{
			const char	*d = "";

			tag = item->item_tags.values[j];

			if (0 == tag->tagid)
			{
				zbx_db_insert_add_values(&db_insert, new_tagid, item->itemid, tag->tag, tag->value);
				zbx_audit_item_update_json_add_item_tag(audit_context_mode, item->itemid, new_tagid,
						item->flags, tag->tag, tag->value);
				new_tagid++;

				continue;
			}

			if (0 == (tag->flags & ZBX_FLAG_DB_TAG_UPDATE))
				continue;

			zbx_audit_item_update_json_update_item_tag_create_entry(audit_context_mode, item->itemid,
					item->flags, tag->tagid);
			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update item_tag set ");

			if (0 != (tag->flags & ZBX_FLAG_DB_TAG_UPDATE_TAG))
			{
				char	*tag_esc;

				tag_esc = zbx_db_dyn_escape_string(tag->tag);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%stag='%s'", d, tag_esc);

				d = ",";
				zbx_audit_item_update_json_update_item_tag_tag(audit_context_mode, item->itemid,
						item->flags, tag->tagid, tag->tag_orig, tag->tag);
				zbx_free(tag_esc);

			}

			if (0 != (tag->flags & ZBX_FLAG_DB_TAG_UPDATE_VALUE))
			{
				char	*value_esc;

				value_esc = zbx_db_dyn_escape_string(tag->value);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%svalue='%s'", d, value_esc);
				zbx_audit_item_update_json_update_item_tag_value(audit_context_mode, item->itemid,
						item->flags, tag->tagid, tag->value_orig, tag->value);

				zbx_free(value_esc);
			}

			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where itemtagid=" ZBX_FS_UI64 ";\n",
					tag->tagid);

			zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}

	}

	if (0 != update_tag_num)
	{
		zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

		if (16 < sql_offset)	/* in ORACLE always present begin..end; */
			zbx_db_execute("%s", sql);
	}

	if (0 != new_tag_num)
	{
		zbx_db_insert_execute(&db_insert);
		zbx_db_insert_clean(&db_insert);
	}

	if (0 != deleteids.values_num)
	{
		zbx_vector_uint64_sort(&deleteids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

		sql_offset = 0;
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_tag where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemtagid", deleteids.values,
				deleteids.values_num);
		zbx_db_execute("%s", sql);

		delete_tag_num = deleteids.values_num;
	}
	zbx_free(sql);
	zbx_vector_uint64_destroy(&deleteids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() added:%d updated:%d removed:%d", __func__, new_tag_num, update_tag_num,
			delete_tag_num);
}

/******************************************************************************
 *                                                                            *
 * Purpose: copies template item script parameters                            *
 *                                                                            *
 * Parameters:                                                                *
 *             items              - [IN] vector of new/updated items          *
 *             audit_context_mode - [IN]                                      *
 *                                                                            *
 ******************************************************************************/
static void	copy_template_item_script_params(const zbx_vector_ptr_t *items, int audit_context_mode)
{
	int				i, j, new_param_num = 0, update_param_num = 0, delete_param_num = 0;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			item_parameter_id = 0;
	zbx_template_item_t		*item;
	zbx_item_param_t		*param;
	zbx_vector_uint64_t		deleteids;
	zbx_db_insert_t			db_insert;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&deleteids);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_params.values_num; j++)
		{
			param = item->item_params.values[j];

			if (0 != (param->flags & ZBX_FLAG_ITEM_PARAM_DELETE))
			{
				zbx_vector_uint64_append(&deleteids, param->item_parameterid);
				zbx_audit_item_delete_params(audit_context_mode, item->itemid, item->flags,
						param->item_parameterid);
				continue;
			}

			if (0 == param->item_parameterid)
			{
				new_param_num++;
				continue;
			}

			if (0 == (param->flags & ZBX_FLAG_ITEM_PARAM_UPDATE))
				continue;

			update_param_num++;
		}
	}

	if (0 != update_param_num)
		zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

	if (0 != new_param_num)
	{
		zbx_db_insert_prepare(&db_insert, "item_parameter", "item_parameterid", "itemid", "name", "value",
				(char *)NULL);
		item_parameter_id = zbx_db_get_maxid_num("item_parameter", new_param_num);
	}

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_params.values_num; j++)
		{
			const char	*d = "";

			param = item->item_params.values[j];

			if (0 == param->item_parameterid)
			{
				zbx_db_insert_add_values(&db_insert, item_parameter_id, item->itemid, param->name,
						param->value);
				zbx_audit_item_update_json_add_params(audit_context_mode, item->itemid, item->flags,
						item_parameter_id,
						param->name, param->value);
				item_parameter_id++;
				continue;
			}

			if (0 == (param->flags & ZBX_FLAG_ITEM_PARAM_UPDATE))
				continue;

			zbx_audit_item_update_json_update_params_create_entry(audit_context_mode, item->itemid,
					item->flags, param->item_parameterid);

			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update item_parameter set ");

			if (0 != (param->flags & ZBX_FLAG_ITEM_PARAM_UPDATE_NAME))
			{
				char	*name_esc;

				name_esc = zbx_db_dyn_escape_string(param->name);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%sname='%s'", d, name_esc);

				zbx_free(name_esc);
				d = ",";

				zbx_audit_item_update_json_update_params_name(audit_context_mode, item->itemid,
						item->flags, param->item_parameterid, param->name_orig, param->name);
			}

			if (0 != (param->flags & ZBX_FLAG_ITEM_PARAM_UPDATE_VALUE))
			{
				char	*value_esc;

				value_esc = zbx_db_dyn_escape_string(param->value);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%svalue='%s'", d, value_esc);

				zbx_free(value_esc);

				zbx_audit_item_update_json_update_params_value(audit_context_mode, item->itemid,
						item->flags, param->item_parameterid, param->value_orig, param->value);
			}

			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where item_parameterid=" ZBX_FS_UI64 ";\n",
					param->item_parameterid);

			zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}

	}

	if (0 != update_param_num)
	{
		zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

		if (16 < sql_offset)	/* in ORACLE always present begin..end; */
			zbx_db_execute("%s", sql);
	}

	if (0 != new_param_num)
	{
		zbx_db_insert_execute(&db_insert);
		zbx_db_insert_clean(&db_insert);
	}

	if (0 != deleteids.values_num)
	{
		sql_offset = 0;
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from item_parameter where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "item_parameterid", deleteids.values,
				deleteids.values_num);
		zbx_db_execute("%s", sql);

		delete_param_num = deleteids.values_num;
	}
	zbx_free(sql);
	zbx_vector_uint64_destroy(&deleteids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() added:%d updated:%d removed:%d", __func__, new_param_num,
			update_param_num, delete_param_num);
}

/******************************************************************************
 *                                                                            *
 * Purpose: copies template discovery item lld macro paths                    *
 *                                                                            *
 * Parameters:                                                                *
 *             items              - [IN] vector of new/updated items          *
 *             audit_context_mode - [IN]                                      *
 *                                                                            *
 ******************************************************************************/
static void	copy_template_lld_macro_paths(const zbx_vector_ptr_t *items, int audit_context_mode)
{
	int				i, j, new_lld_macro_num = 0, update_lld_macro_num = 0, delete_lld_macro_num = 0;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			new_lld_macro_pathid = 0;
	zbx_template_item_t		*item;
	zbx_template_lld_macro_t	*lld_macro;
	zbx_vector_uint64_t		deleteids;
	zbx_db_insert_t			db_insert;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&deleteids);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_lld_macros.values_num; j++)
		{
			lld_macro = (zbx_template_lld_macro_t *)item->item_lld_macros.values[j];

			if (0 != (lld_macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_DELETE))
			{
				zbx_vector_uint64_append(&deleteids, lld_macro->lld_macro_pathid);
				zbx_audit_discovery_rule_update_json_delete_lld_macro_path(audit_context_mode,
						item->itemid, lld_macro->lld_macro_pathid);
				continue;
			}

			if (0 == lld_macro->lld_macro_pathid)
			{
				new_lld_macro_num++;
				continue;
			}

			if (0 == (lld_macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE))
				continue;

			update_lld_macro_num++;
		}
	}

	if (0 != deleteids.values_num)
	{
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from lld_macro_path where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "lld_macro_pathid", deleteids.values,
				deleteids.values_num);
		zbx_db_execute("%s", sql);

		delete_lld_macro_num = deleteids.values_num;
		sql_offset = 0;
	}

	if (0 != update_lld_macro_num)
		zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

	if (0 != new_lld_macro_num)
	{
		zbx_db_insert_prepare(&db_insert, "lld_macro_path", "lld_macro_pathid", "itemid", "lld_macro", "path",
				(char *)NULL);

		new_lld_macro_pathid = zbx_db_get_maxid_num("lld_macro_path", new_lld_macro_num);
	}

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		for (j = 0; j < item->item_lld_macros.values_num; j++)
		{
			const char	*d = "";

			lld_macro = (zbx_template_lld_macro_t *)item->item_lld_macros.values[j];
			if (0 == lld_macro->lld_macro_pathid)
			{
				zbx_db_insert_add_values(&db_insert, new_lld_macro_pathid, item->itemid,
						lld_macro->lld_macro, lld_macro->path);

				zbx_audit_discovery_rule_update_json_add_lld_macro_path(audit_context_mode,
						item->itemid, new_lld_macro_pathid, lld_macro->lld_macro,
						lld_macro->path);

				new_lld_macro_pathid++;
				continue;
			}

			if (0 == (lld_macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE))
				continue;

			zbx_audit_discovery_rule_update_json_lld_macro_path_create_update_entry(audit_context_mode,
					item->itemid, lld_macro->lld_macro_pathid);

			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update lld_macro_path set ");

			if (0 != (lld_macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_LLD_MACRO))
			{
				char	*lld_macro_esc;

				lld_macro_esc = zbx_db_dyn_escape_string(lld_macro->lld_macro);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%slld_macro='%s'", d, lld_macro_esc);

				zbx_free(lld_macro_esc);
				d = ",";

				zbx_audit_discovery_rule_update_json_update_lld_macro_path_lld_macro(audit_context_mode,
						item->itemid, lld_macro->lld_macro_pathid, lld_macro->lld_macro_orig,
						lld_macro->lld_macro);
			}

			if (0 != (lld_macro->upd_flags & ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_PATH))
			{
				char	*path_esc;

				path_esc = zbx_db_dyn_escape_string(lld_macro->path);
				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%spath='%s'", d, path_esc);

				zbx_free(path_esc);

				zbx_audit_discovery_rule_update_json_update_lld_macro_path_path(audit_context_mode,
						item->itemid, lld_macro->lld_macro_pathid, lld_macro->path_orig,
						lld_macro->path);
			}

			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where lld_macro_pathid=" ZBX_FS_UI64 ";\n",
					lld_macro->lld_macro_pathid);

			zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}

	}

	if (0 != update_lld_macro_num)
	{
		zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

		if (16 < sql_offset)	/* in ORACLE always present begin..end; */
			zbx_db_execute("%s", sql);
	}

	if (0 != new_lld_macro_num)
	{
		zbx_db_insert_execute(&db_insert);
		zbx_db_insert_clean(&db_insert);
	}

	zbx_free(sql);
	zbx_vector_uint64_destroy(&deleteids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() added:%d updated:%d removed:%d", __func__, new_lld_macro_num,
			update_lld_macro_num, delete_lld_macro_num);
}

static void	lld_override_condition_free(lld_override_codition_t *override_condition)
{
	zbx_free(override_condition->macro);
	zbx_free(override_condition->value);
	zbx_free(override_condition);
}

static void	lld_override_free(lld_override_t *override)
{
	zbx_vector_ptr_clear_ext(&override->override_conditions, (zbx_clean_func_t)lld_override_condition_free);
	zbx_vector_ptr_destroy(&override->override_conditions);
	zbx_vector_lld_override_operation_clear_ext(&override->override_operations, zbx_lld_override_operation_free);
	zbx_vector_lld_override_operation_destroy(&override->override_operations);
	zbx_free(override->name);
	zbx_free(override->formula);
	zbx_free(override);
}

static void	lld_override_conditions_load(zbx_vector_ptr_t *overrides, const zbx_vector_uint64_t *overrideids,
		char **sql, size_t *sql_alloc)
{
	size_t			sql_offset = 0;
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	zbx_uint64_t		overrideid;
	int			i;
	lld_override_t		*override;
	lld_override_codition_t	*override_condition;

	zbx_snprintf_alloc(sql, sql_alloc, &sql_offset,
		"select lld_overrideid,lld_override_conditionid,operator,macro,value"
			" from lld_override_condition"
			" where");
	zbx_db_add_condition_alloc(sql, sql_alloc, &sql_offset, "lld_overrideid", overrideids->values,
			overrideids->values_num);

	result = zbx_db_select("%s", *sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_STR2UINT64(overrideid, row[0]);

		if (FAIL == (i = zbx_vector_ptr_bsearch(overrides, &overrideid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		override = (lld_override_t *)overrides->values[i];

		override_condition = (lld_override_codition_t *)zbx_malloc(NULL, sizeof(lld_override_codition_t));
		ZBX_STR2UINT64(override_condition->override_conditionid, row[1]);
		ZBX_STR2UCHAR(override_condition->operator, row[2]);
		override_condition->macro = zbx_strdup(NULL, row[3]);
		override_condition->value = zbx_strdup(NULL, row[4]);

		zbx_vector_ptr_append(&override->override_conditions, override_condition);
	}
	zbx_db_free_result(result);
}

static void	lld_override_operations_load(zbx_vector_ptr_t *overrides, const zbx_vector_uint64_t *overrideids,
		char **sql, size_t *sql_alloc)
{
	zbx_vector_lld_override_operation_t	ops;
	int					i, index;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_lld_override_operation_create(&ops);

	zbx_load_lld_override_operations(overrideids, sql, sql_alloc, &ops);

	for (i = 0; i < ops.values_num; i++)
	{
		lld_override_t			*override;
		zbx_lld_override_operation_t	*op = ops.values[i];

		if (FAIL == (index = zbx_vector_ptr_bsearch(overrides, &op->overrideid,
				ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
		{
			zbx_lld_override_operation_free(op);
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}
		override = overrides->values[index];
		zbx_vector_lld_override_operation_append(&override->override_operations, op);
	}

	zbx_vector_lld_override_operation_destroy(&ops);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

static void	save_template_lld_overrides(zbx_vector_ptr_t *overrides, zbx_hashset_t *lld_items,
		int audit_context_mode)
{
	zbx_uint64_t			overrideid, override_operationid = 0, override_conditionid = 0;
	zbx_db_insert_t			db_insert, db_insert_oconditions, db_insert_ooperations, db_insert_opstatus,
					db_insert_opdiscover, db_insert_opperiod, db_insert_ophistory,
					db_insert_optrends, db_insert_opseverity, db_insert_optag, db_insert_optemplate,
					db_insert_opinventory;
	int				i, j, k, conditions_num, operations_num;
	lld_override_t			*override;
	lld_override_codition_t		*override_condition;
	zbx_lld_override_operation_t	*override_operation;
	const zbx_template_item_t	**pitem;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	if (0 != overrides->values_num)
		overrideid = zbx_db_get_maxid_num("lld_override", overrides->values_num);

	zbx_db_insert_prepare(&db_insert, "lld_override", "lld_overrideid", "itemid", "name", "step", "evaltype",
			"formula", "stop", (char *)NULL);

	zbx_db_insert_prepare(&db_insert_oconditions, "lld_override_condition", "lld_override_conditionid",
			"lld_overrideid", "operator", "macro", "value", (char *)NULL);

	for (i = 0, operations_num = 0, conditions_num = 0; i < overrides->values_num; i++)
	{
		override = (lld_override_t *)overrides->values[i];
		operations_num += override->override_operations.values_num;
		conditions_num += override->override_conditions.values_num;
	}

	if (0 != operations_num)
		override_operationid = zbx_db_get_maxid_num("lld_override_operation", operations_num);

	if (0 != conditions_num)
		override_conditionid = zbx_db_get_maxid_num("lld_override_condition", conditions_num);

	zbx_db_insert_prepare(&db_insert_ooperations, "lld_override_operation", "lld_override_operationid",
				"lld_overrideid", "operationobject", "operator", "value", (char *)NULL);

	zbx_db_insert_prepare(&db_insert_opstatus, "lld_override_opstatus", "lld_override_operationid", "status",
			(char *)NULL);

	zbx_db_insert_prepare(&db_insert_opdiscover, "lld_override_opdiscover", "lld_override_operationid", "discover",
			(char *)NULL);

	zbx_db_insert_prepare(&db_insert_opperiod, "lld_override_opperiod", "lld_override_operationid", "delay",
			(char *)NULL);
	zbx_db_insert_prepare(&db_insert_ophistory, "lld_override_ophistory", "lld_override_operationid", "history",
			(char *)NULL);
	zbx_db_insert_prepare(&db_insert_optrends, "lld_override_optrends", "lld_override_operationid", "trends",
			(char *)NULL);

	zbx_db_insert_prepare(&db_insert_opseverity, "lld_override_opseverity", "lld_override_operationid", "severity",
			(char *)NULL);
	zbx_db_insert_prepare(&db_insert_optag, "lld_override_optag", "lld_override_optagid",
			"lld_override_operationid", "tag", "value", (char *)NULL);

	zbx_db_insert_prepare(&db_insert_optemplate, "lld_override_optemplate", "lld_override_optemplateid",
				"lld_override_operationid", "templateid", (char *)NULL);
	zbx_db_insert_prepare(&db_insert_opinventory, "lld_override_opinventory", "lld_override_operationid",
			"inventory_mode", (char *)NULL);

	for (i = 0; i < overrides->values_num; i++)
	{
		zbx_template_item_t	item_local, *pitem_local = &item_local;

		override = (lld_override_t *)overrides->values[i];

		item_local.templateid = override->itemid;
		if (NULL == (pitem = (const zbx_template_item_t **)zbx_hashset_search(lld_items, &pitem_local)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		for (j = 0; j < override->override_conditions.values_num; j++)
		{
			override_condition = (lld_override_codition_t *)override->override_conditions.values[j];

			zbx_db_insert_add_values(&db_insert_oconditions, override_conditionid, overrideid,
					(int)override_condition->operator, override_condition->macro,
					override_condition->value);

			zbx_audit_discovery_rule_update_json_add_lld_override_condition(audit_context_mode, (*pitem)->itemid,
					overrideid,
				override_conditionid, (int)override_condition->operator, override_condition->macro,
				override_condition->value);

			if (ZBX_CONDITION_EVAL_TYPE_EXPRESSION == override->evaltype)
			{
				update_template_lld_formula(&override->formula,
						override_condition->override_conditionid, override_conditionid);
			}

			override_conditionid++;
		}

		/* prepare lld_override insert after formula is updated */
		zbx_db_insert_add_values(&db_insert, overrideid, (*pitem)->itemid, override->name, (int)override->step,
				(int)override->evaltype, override->formula, (int)override->stop);

		zbx_audit_discovery_rule_update_json_add_lld_override(audit_context_mode, (*pitem)->itemid, overrideid,
				override->name, (int)override->step, (int)override->stop);

		zbx_audit_discovery_rule_update_json_add_lld_override_filter(audit_context_mode, (*pitem)->itemid,
				overrideid, (int)override->evaltype, override->formula);

		for (j = 0; j < override->override_operations.values_num; j++)
		{
			override_operation = (zbx_lld_override_operation_t *)override->override_operations.values[j];

			zbx_db_insert_add_values(&db_insert_ooperations, override_operationid, overrideid,
					(int)override_operation->operationtype, (int)override_operation->operator,
					override_operation->value);

			zbx_audit_discovery_rule_update_json_add_lld_override_operation(audit_context_mode,
					(*pitem)->itemid, overrideid, override_operationid,
					(int)override_operation->operator, override_operation->value);

			if (ZBX_PROTOTYPE_STATUS_COUNT != override_operation->status)
			{
				zbx_db_insert_add_values(&db_insert_opstatus, override_operationid,
						(int)override_operation->status);

				zbx_audit_discovery_rule_update_json_add_lld_override_opstatus(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						(int)override_operation->status);
			}

			if (ZBX_PROTOTYPE_DISCOVER_COUNT != override_operation->discover)
			{
				zbx_db_insert_add_values(&db_insert_opdiscover, override_operationid,
						(int)override_operation->discover);

				zbx_audit_discovery_rule_update_json_add_lld_override_opdiscover(audit_context_mode,
						(*pitem)->itemid, overrideid,override_operationid,
						(int)override_operation->discover);
			}

			if (NULL != override_operation->delay)
			{
				zbx_db_insert_add_values(&db_insert_opperiod, override_operationid,
						override_operation->delay);

				zbx_audit_discovery_rule_update_json_add_lld_override_opperiod(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						override_operation->delay);
			}

			if (NULL != override_operation->history)
			{
				zbx_db_insert_add_values(&db_insert_ophistory, override_operationid,
						override_operation->history);

				zbx_audit_discovery_rule_update_json_add_lld_override_ophistory(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						override_operation->history);
			}

			if (NULL != override_operation->trends)
			{
				zbx_db_insert_add_values(&db_insert_optrends, override_operationid,
						override_operation->trends);

				zbx_audit_discovery_rule_update_json_add_lld_override_optrends(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						override_operation->trends);
			}

			if (TRIGGER_SEVERITY_COUNT != override_operation->severity)
			{
				zbx_db_insert_add_values(&db_insert_opseverity, override_operationid,
						(int)override_operation->severity);

				zbx_audit_discovery_rule_update_json_add_lld_override_opseverity(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						override_operation->severity);
			}

			if (0 != override_operation->tags.values_num)
			{
				zbx_uint64_t	lld_override_optagid;

				lld_override_optagid = zbx_db_get_maxid_num("lld_override_optag",
						override_operation->tags.values_num);

				for (k = 0; k < override_operation->tags.values_num; k++)
				{
					zbx_db_tag_t	*tag = override_operation->tags.values[k];

					zbx_db_insert_add_values(&db_insert_optag, lld_override_optagid,
							override_operationid, tag->tag, tag->value);

					zbx_audit_discovery_rule_update_json_add_lld_override_optag(audit_context_mode,
							(*pitem)->itemid, overrideid, lld_override_optagid, tag->tag,
							tag->value);

					lld_override_optagid++;
				}
			}

			if (0 != override_operation->templateids.values_num)
			{
				zbx_uint64_t	lld_override_optemplateid;

				lld_override_optemplateid = zbx_db_get_maxid_num("lld_override_optemplate",
						override_operation->templateids.values_num);

				for (k = 0; k < override_operation->templateids.values_num; k++)
				{
					zbx_db_insert_add_values(&db_insert_optemplate, lld_override_optemplateid,
							override_operationid,
						override_operation->templateids.values[k]);

					zbx_audit_discovery_rule_update_json_add_lld_override_optemplate(
							audit_context_mode, (*pitem)->itemid, overrideid,
							lld_override_optemplateid,
							override_operation->templateids.values[k]);

					lld_override_optemplateid++;
				}
			}

			if (HOST_INVENTORY_COUNT != override_operation->inventory_mode)
			{
				zbx_db_insert_add_values(&db_insert_opinventory, override_operationid,
						(int)override_operation->inventory_mode);

				zbx_audit_discovery_rule_update_json_add_lld_override_opinventory(audit_context_mode,
						(*pitem)->itemid, overrideid, override_operationid,
						(int)override_operation->inventory_mode);
			}

			override_operationid++;
		}

		overrideid++;
	}

	zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	zbx_db_insert_execute(&db_insert_oconditions);
	zbx_db_insert_clean(&db_insert_oconditions);

	zbx_db_insert_execute(&db_insert_ooperations);
	zbx_db_insert_clean(&db_insert_ooperations);

	zbx_db_insert_execute(&db_insert_opstatus);
	zbx_db_insert_clean(&db_insert_opstatus);

	zbx_db_insert_execute(&db_insert_opdiscover);
	zbx_db_insert_clean(&db_insert_opdiscover);

	zbx_db_insert_execute(&db_insert_opperiod);
	zbx_db_insert_clean(&db_insert_opperiod);

	zbx_db_insert_execute(&db_insert_ophistory);
	zbx_db_insert_clean(&db_insert_ophistory);

	zbx_db_insert_execute(&db_insert_optrends);
	zbx_db_insert_clean(&db_insert_optrends);

	zbx_db_insert_execute(&db_insert_opseverity);
	zbx_db_insert_clean(&db_insert_opseverity);

	zbx_db_insert_execute(&db_insert_optag);
	zbx_db_insert_clean(&db_insert_optag);

	zbx_db_insert_execute(&db_insert_optemplate);
	zbx_db_insert_clean(&db_insert_optemplate);

	zbx_db_insert_execute(&db_insert_opinventory);
	zbx_db_insert_clean(&db_insert_opinventory);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

static void	copy_template_lld_overrides(const zbx_vector_uint64_t *templateids,
		const zbx_vector_uint64_t *lld_itemids, zbx_hashset_t *lld_items, int audit_context_mode)
{
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	lld_override_t		*override;
	zbx_vector_ptr_t	overrides;
	zbx_vector_uint64_t	overrideids;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&overrideids);
	zbx_vector_ptr_create(&overrides);

	/* remove overrides from existing items with same key */
	if (0 != lld_itemids->values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select lld_overrideid,itemid from lld_override where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", lld_itemids->values,
				lld_itemids->values_num);
		result = zbx_db_select("%s", sql);

		while (NULL != (row = zbx_db_fetch(result)))
		{
			zbx_uint64_t	delete_lld_overrideid, delete_itemid;

			ZBX_STR2UINT64(delete_lld_overrideid, row[0]);
			ZBX_STR2UINT64(delete_itemid, row[1]);
			zbx_audit_discovery_rule_update_json_delete_lld_override(audit_context_mode, delete_itemid,
					delete_lld_overrideid);
		}

		sql_offset = 0;
		zbx_db_free_result(result);

		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from lld_override where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", lld_itemids->values,
				lld_itemids->values_num);
		zbx_db_execute("%s", sql);
		sql_offset = 0;
	}

	/* read overrides from templates that should be linked */
	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
		"select l.lld_overrideid,l.itemid,l.name,l.step,l.evaltype,l.formula,l.stop"
			" from lld_override l,items i"
			" where l.itemid=i.itemid"
			" and");
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", templateids->values,
			templateids->values_num);
	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " order by l.lld_overrideid");

	result = zbx_db_select("%s", sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		override = (lld_override_t *)zbx_malloc(NULL, sizeof(lld_override_t));
		ZBX_STR2UINT64(override->overrideid, row[0]);
		ZBX_STR2UINT64(override->itemid, row[1]);
		override->name = zbx_strdup(NULL, row[2]);
		ZBX_STR2UCHAR(override->step, row[3]);
		ZBX_STR2UCHAR(override->evaltype, row[4]);
		override->formula = zbx_strdup(NULL, row[5]);
		ZBX_STR2UCHAR(override->stop, row[6]);
		zbx_vector_ptr_create(&override->override_conditions);
		zbx_vector_lld_override_operation_create(&override->override_operations);

		zbx_vector_uint64_append(&overrideids, override->overrideid);
		zbx_vector_ptr_append(&overrides, override);
	}
	zbx_db_free_result(result);

	if (0 != overrides.values_num)
	{
		lld_override_conditions_load(&overrides, &overrideids, &sql, &sql_alloc);
		lld_override_operations_load(&overrides, &overrideids, &sql, &sql_alloc);
		save_template_lld_overrides(&overrides, lld_items, audit_context_mode);
	}
	zbx_free(sql);

	zbx_vector_uint64_destroy(&overrideids);
	zbx_vector_ptr_clear_ext(&overrides, (zbx_clean_func_t)lld_override_free);
	zbx_vector_ptr_destroy(&overrides);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: compare templateid of two template items                          *
 *                                                                            *
 * Parameters: d1 - [IN] first template item                                  *
 *             d2 - [IN] second template item                                 *
 *                                                                            *
 * Return value: compare result (-1 for d1<d2, 1 for d1>d2, 0 for d1==d2)     *
 *                                                                            *
 ******************************************************************************/
static int	compare_template_items(const void *d1, const void *d2)
{
	const zbx_template_item_t	*i1 = *(const zbx_template_item_t * const *)d1;
	const zbx_template_item_t	*i2 = *(const zbx_template_item_t * const *)d2;

	return zbx_default_uint64_compare_func(&i1->templateid, &i2->templateid);
}

/******************************************************************************
 *                                                                            *
 * Purpose: create dependent item index in master item data                   *
 *                                                                            *
 * Parameters: items       - [IN/OUT] the template items                      *
 *                                                                            *
 ******************************************************************************/
static void	link_template_dependent_items(zbx_vector_ptr_t *items)
{
	zbx_template_item_t	*item, *master, item_local;
	int			i, index;
	zbx_vector_ptr_t	template_index;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_ptr_create(&template_index);
	zbx_vector_ptr_append_array(&template_index, items->values, items->values_num);
	zbx_vector_ptr_sort(&template_index, compare_template_items);

	for (i = items->values_num - 1; i >= 0; i--)
	{
		item = (zbx_template_item_t *)items->values[i];
		if (0 != item->master_itemid)
		{
			item_local.templateid = item->master_itemid;
			if (FAIL == (index = zbx_vector_ptr_bsearch(&template_index, &item_local,
					compare_template_items)))
			{
				/* dependent item without master item should be removed */
				THIS_SHOULD_NEVER_HAPPEN;
				free_template_item(item);
				zbx_vector_ptr_remove(items, i);
			}
			else
			{
				master = (zbx_template_item_t *)template_index.values[index];
				zbx_vector_ptr_append(&master->dependent_items, item);
			}
		}
	}

	zbx_vector_ptr_destroy(&template_index);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

static int	template_item_preproc_sort_by_step(const void *d1, const void *d2)
{
	zbx_template_item_preproc_t *op1 = *(zbx_template_item_preproc_t * const *)d1;
	zbx_template_item_preproc_t *op2 = *(zbx_template_item_preproc_t * const *)d2;

	ZBX_RETURN_IF_NOT_EQUAL(op1->step, op2->step);

	return 0;
}

static int	template_lld_macro_sort_by_macro(const void *d1, const void *d2)
{
	zbx_template_lld_macro_t	*ip1 = *(zbx_template_lld_macro_t * const *)d1;
	zbx_template_lld_macro_t	*ip2 = *(zbx_template_lld_macro_t * const *)d2;

	ZBX_RETURN_IF_NOT_EQUAL(ip1->lld_macro, ip2->lld_macro);

	return 0;
}
/******************************************************************************
 *                                                                            *
 * Purpose: create item_preproc vectors in item data                          *
 *                                                                            *
 * Parameters: templateids - [IN] vector of template IDs                      *
 *             items       - [IN/OUT] the template items                      *
 *                                                                            *
 ******************************************************************************/
static void	link_template_items_preproc(const zbx_vector_uint64_t *templateids, zbx_vector_ptr_t *items)
{
	int				i, index;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			itemid;
	zbx_template_item_preproc_t	*ppsrc, *ppdst;
	zbx_template_item_t		*item;
	zbx_hashset_t			items_t;
	zbx_vector_uint64_t		itemids;
	zbx_db_row_t			row;
	zbx_db_result_t			result;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&itemids);
	zbx_hashset_create(&items_t, (size_t)items->values_num, template_item_hash_func, template_item_compare_func);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		if (NULL == item->key)
			zbx_vector_uint64_append(&itemids, item->itemid);

		zbx_hashset_insert(&items_t, &item, sizeof(zbx_template_item_t *));
	}

	if (0 != itemids.values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
				"select ip.item_preprocid,ip.itemid,ip.step,ip.type,ip.params,ip.error_handler,"
					"ip.error_handler_params"
				" from item_preproc ip"
				" where ");

		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids.values, itemids.values_num);

		result = zbx_db_select("%s", sql);
		while (NULL != (row = zbx_db_fetch(result)))
		{
			ZBX_STR2UINT64(itemid, row[1]);

			if (FAIL == (index = zbx_vector_ptr_bsearch(items, &itemid,
					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
			{
				THIS_SHOULD_NEVER_HAPPEN;
				continue;
			}

			item = (zbx_template_item_t *)items->values[index];

			ppdst = zbx_item_preproc_create(row[0], atoi(row[2]), atoi(row[3]), row[4], atoi(row[5]),
					row[6]);

			zbx_vector_item_preproc_ptr_append(&((zbx_template_item_t *)item)->item_preprocs, ppdst);
		}
		zbx_db_free_result(result);
		zbx_free(sql);
		sql_offset = 0;
		sql_alloc = 0;
	}

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
			"select ip.item_preprocid,ip.itemid,ip.step,ip.type,ip.params,ip.error_handler,"
				"ip.error_handler_params"
			" from item_preproc ip,items ti"
			" where ip.itemid=ti.itemid"
			" and");

	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "ti.hostid", templateids->values,
			templateids->values_num);

	result = zbx_db_select("%s", sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_template_item_t		item_local, *pitem_local = &item_local, **pitem;

		ZBX_STR2UINT64(item_local.templateid, row[1]);
		if (NULL == (pitem = (zbx_template_item_t **)zbx_hashset_search(&items_t, &pitem_local)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		ppdst = zbx_item_preproc_create(row[0], atoi(row[2]), atoi(row[3]), row[4], atoi(row[5]), row[6]);

		zbx_vector_item_preproc_ptr_append(&(*pitem)->template_preprocs, ppdst);
	}
	zbx_db_free_result(result);
	zbx_free(sql);

	for (i = 0; i < items->values_num; i++)
	{
		int	j, preproc_num;
		char	*buffer = NULL;

		item = (zbx_template_item_t *)items->values[i];

		zbx_vector_item_preproc_ptr_sort(&item->item_preprocs, template_item_preproc_sort_by_step);
		zbx_vector_item_preproc_ptr_sort(&item->template_preprocs, template_item_preproc_sort_by_step);

		preproc_num = MAX(item->item_preprocs.values_num, item->template_preprocs.values_num);

		for (j = 0; j < preproc_num; j++)
		{
			if (j >= item->item_preprocs.values_num)
			{
				ppsrc = (zbx_template_item_preproc_t *)item->template_preprocs.values[j];

				ppdst = zbx_item_preproc_create("0", ppsrc->step, ppsrc->type, ppsrc->params,
						ppsrc->error_handler, ppsrc->error_handler_params);

				zbx_vector_item_preproc_ptr_append(&item->item_preprocs, ppdst);
				continue;
			}

			ppdst = (zbx_template_item_preproc_t *)item->item_preprocs.values[j];

			if (j >= item->template_preprocs.values_num)
			{
				ppdst->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_PREPROC_DELETE;
				continue;
			}

			ppsrc = (zbx_template_item_preproc_t *)item->template_preprocs.values[j];

			if (ppdst->type != ppsrc->type)
			{
				ppdst->type_orig = ppdst->type;
				ppdst->type = ppsrc->type;
				ppdst->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_TYPE;
			}
			buffer = zbx_strdup(buffer, ppsrc->params);

			if (0 != strcmp(ppdst->params, buffer))
			{
				ppdst->params_orig = zbx_strdup(NULL, ppdst->params);
				zbx_free(ppdst->params);
				ppdst->params = buffer;
				buffer = NULL;
				ppdst->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_PARAMS;
			}

			if (ppdst->error_handler != ppsrc->error_handler)
			{
				ppdst->error_handler_orig = ppdst->error_handler;
				ppdst->error_handler = ppsrc->error_handler;
				ppdst->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER;
			}

			buffer = zbx_strdup(buffer, ppsrc->error_handler_params);

			if (0 != strcmp(ppdst->error_handler_params, buffer))
			{
				ppdst->error_handler_params_orig = zbx_strdup(NULL, ppdst->error_handler_params);
				zbx_free(ppdst->error_handler_params);
				ppdst->error_handler_params = buffer;
				buffer = NULL;
				ppdst->upd_flags |= ZBX_FLAG_TEMPLATE_ITEM_PREPROC_UPDATE_ERROR_HANDLER_PARAMS;
			}
			else
				zbx_free(buffer);
		}
	}
	zbx_hashset_destroy(&items_t);
	zbx_vector_uint64_destroy(&itemids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: create item_tags vectors in item data                             *
 *                                                                            *
 * Parameters: templateids - [IN] vector of template IDs                      *
 *             items       - [IN/OUT] the template items                      *
 *                                                                            *
 ******************************************************************************/
static void	link_template_items_tag(const zbx_vector_uint64_t *templateids, zbx_vector_ptr_t *items)
{
	int				i, index;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			itemid;
	zbx_db_tag_t			*db_tag;
	zbx_template_item_t		*item;
	zbx_hashset_t			items_t;
	zbx_vector_uint64_t		itemids;
	zbx_db_row_t			row;
	zbx_db_result_t			result;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&itemids);
	zbx_hashset_create(&items_t, (size_t)items->values_num, template_item_hash_func, template_item_compare_func);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		if (NULL == item->key)
			zbx_vector_uint64_append(&itemids, item->itemid);

		zbx_hashset_insert(&items_t, &item, sizeof(zbx_template_item_t *));
	}

	if (0 != itemids.values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
				"select it.itemtagid,it.itemid,it.tag,it.value"
				" from item_tag it"
				" where ");

		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids.values, itemids.values_num);

		result = zbx_db_select("%s", sql);
		while (NULL != (row = zbx_db_fetch(result)))
		{
			ZBX_STR2UINT64(itemid, row[1]);

			if (FAIL == (index = zbx_vector_ptr_bsearch(items, &itemid,
					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
			{
				THIS_SHOULD_NEVER_HAPPEN;
				continue;
			}

			item = (zbx_template_item_t *)items->values[index];
			db_tag = zbx_db_tag_create(row[2], row[3]);
			ZBX_STR2UINT64(db_tag->tagid, row[0]);
			zbx_vector_db_tag_ptr_append(&item->item_tags, db_tag);
		}

		zbx_db_free_result(result);
		zbx_free(sql);
		sql_offset = 0;
		sql_alloc = 0;
	}

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
			"select it.itemid,it.tag,it.value"
			" from item_tag it,items ti"
			" where it.itemid=ti.itemid"
			" and");

	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "ti.hostid", templateids->values,
			templateids->values_num);

	result = zbx_db_select("%s", sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_template_item_t		item_local, *pitem_local = &item_local, **pitem;

		ZBX_STR2UINT64(item_local.templateid, row[0]);

		if (NULL == (pitem = (zbx_template_item_t **)zbx_hashset_search(&items_t, &pitem_local)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		db_tag = zbx_db_tag_create(row[1], row[2]);
		zbx_vector_db_tag_ptr_append(&(*pitem)->template_tags, db_tag);
	}
	zbx_db_free_result(result);
	zbx_free(sql);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];
		(void)zbx_merge_tags(&item->item_tags, &item->template_tags, NULL, NULL);
	}
	zbx_hashset_destroy(&items_t);
	zbx_vector_uint64_destroy(&itemids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: create item_params vectors in item data                           *
 *                                                                            *
 * Parameters: templateids - [IN] vector of template IDs                      *
 *             items       - [IN/OUT] the template items                      *
 *                                                                            *
 ******************************************************************************/
static void	link_template_items_param(const zbx_vector_uint64_t *templateids, zbx_vector_ptr_t *items)
{
	int				i, index;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			itemid;
	zbx_item_param_t		*db_item_param;
	zbx_template_item_t		*item;
	zbx_hashset_t			items_t;
	zbx_vector_uint64_t		itemids;
	zbx_db_row_t			row;
	zbx_db_result_t			result;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_uint64_create(&itemids);
	zbx_hashset_create(&items_t, (size_t)items->values_num, template_item_hash_func, template_item_compare_func);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];

		if (NULL == item->key)
			zbx_vector_uint64_append(&itemids, item->itemid);

		zbx_hashset_insert(&items_t, &item, sizeof(zbx_template_item_t *));
	}

	if (0 != itemids.values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
				"select ip.item_parameterid,ip.itemid,ip.name,ip.value"
				" from item_parameter ip"
				" where ");

		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", itemids.values, itemids.values_num);

		result = zbx_db_select("%s", sql);
		while (NULL != (row = zbx_db_fetch(result)))
		{
			ZBX_STR2UINT64(itemid, row[1]);

			if (FAIL == (index = zbx_vector_ptr_bsearch(items, &itemid,
					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
			{
				THIS_SHOULD_NEVER_HAPPEN;
				continue;
			}

			item = (zbx_template_item_t *)items->values[index];

			db_item_param = zbx_item_param_create(row[2], row[3]);
			ZBX_STR2UINT64(db_item_param->item_parameterid, row[0]);
			zbx_vector_item_param_ptr_append(&item->item_params, db_item_param);
		}
		zbx_db_free_result(result);
		zbx_free(sql);
		sql_offset = 0;
		sql_alloc = 0;
	}

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
			"select ip.itemid,ip.name,ip.value"
			" from item_parameter ip,items ti"
			" where ip.itemid=ti.itemid"
			" and");

	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "ti.hostid", templateids->values,
			templateids->values_num);

	result = zbx_db_select("%s", sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_template_item_t	item_local, *pitem_local = &item_local, **pitem;

		ZBX_STR2UINT64(item_local.templateid, row[0]);
		if (NULL == (pitem = (zbx_template_item_t **)zbx_hashset_search(&items_t, &pitem_local)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}

		db_item_param = zbx_item_param_create(row[1], row[2]);
		zbx_vector_item_param_ptr_append(&(*pitem)->template_params, db_item_param);
	}

	zbx_db_free_result(result);
	zbx_free(sql);

	for (i = 0; i < items->values_num; i++)
	{
		item = (zbx_template_item_t *)items->values[i];
		zbx_merge_item_params(&item->item_params, &item->template_params, NULL);
	}
	zbx_hashset_destroy(&items_t);
	zbx_vector_uint64_destroy(&itemids);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}

/******************************************************************************
 *                                                                            *
 * Purpose: create lld_macro vectors in item data                             *
 *                                                                            *
 * Parameters: templateids - [IN] vector of template IDs                      *
 *             lld_itemids - [IN] the template discovery item ids             *
 *             lld_items   - [IN/OUT] the template discovery items            *
 *             items       - [IN/OUT] the template items                      *
 *                                                                            *
 ******************************************************************************/
static void	link_template_lld_macro_paths(const zbx_vector_uint64_t *templateids,
		const zbx_vector_uint64_t *lld_itemids, zbx_hashset_t *lld_items,  zbx_vector_ptr_t *items)
{
	int				i, index;
	char				*sql = NULL;
	size_t				sql_alloc = 0, sql_offset = 0;
	zbx_uint64_t			itemid;
	zbx_template_lld_macro_t	*plmpsrc, *plmpdst;
	zbx_template_item_t		*item;
	zbx_db_row_t			row;
	zbx_db_result_t			result;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	if (0 != lld_itemids->values_num)
	{
		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
				"select l.lld_macro_pathid,l.itemid,l.lld_macro,l.path"
				" from lld_macro_path l"
				" where ");

		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "itemid", lld_itemids->values,
				lld_itemids->values_num);

		result = zbx_db_select("%s", sql);
		while (NULL != (row = zbx_db_fetch(result)))
		{
			ZBX_STR2UINT64(itemid, row[1]);

			if (FAIL == (index = zbx_vector_ptr_bsearch(items, &itemid,
					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC)))
			{
				THIS_SHOULD_NEVER_HAPPEN;
				continue;
			}

			item = (zbx_template_item_t *)items->values[index];

			plmpdst = (zbx_template_lld_macro_t *)zbx_malloc(NULL, sizeof(zbx_template_lld_macro_t));

			plmpdst->upd_flags = ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_RESET_FLAG;
			ZBX_STR2UINT64(plmpdst->lld_macro_pathid, row[0]);
			plmpdst->lld_macro = zbx_strdup(NULL, row[2]);
			plmpdst->path = zbx_strdup(NULL, row[3]);

			zbx_vector_lld_macro_ptr_append(&item->item_lld_macros, plmpdst);
		}
		zbx_db_free_result(result);
		zbx_free(sql);
		sql_offset = 0;
		sql_alloc = 0;
	}

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
			"select l.lld_macro_pathid,l.itemid,l.lld_macro,l.path"
			" from lld_macro_path l,items i"
			" where l.itemid=i.itemid"
			" and");

	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i.hostid", templateids->values,
			templateids->values_num);

	result = zbx_db_select("%s", sql);
	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_template_item_t	item_local, *pitem_local = &item_local, **pitem;

		ZBX_STR2UINT64(item_local.templateid, row[1]);
		if (NULL == (pitem = (zbx_template_item_t **)zbx_hashset_search(lld_items, &pitem_local)))
		{
			THIS_SHOULD_NEVER_HAPPEN;
			continue;
		}
		plmpdst = (zbx_template_lld_macro_t *)zbx_malloc(NULL, sizeof(zbx_template_lld_macro_t));

		plmpdst->upd_flags = ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_RESET_FLAG;
		ZBX_STR2UINT64(plmpdst->lld_macro_pathid, row[0]);
		plmpdst->lld_macro = zbx_strdup(NULL, row[2]);
		plmpdst->path = zbx_strdup(NULL, row[3]);

		zbx_vector_lld_macro_ptr_append(&(*pitem)->template_lld_macros, plmpdst);
	}
	zbx_db_free_result(result);
	zbx_free(sql);

	for (i = 0; i < items->values_num; i++)
	{
		int	j, lld_macro_num;
		char	*buffer = NULL;

		item = (zbx_template_item_t *)items->values[i];

		zbx_vector_lld_macro_ptr_sort(&item->item_lld_macros, template_lld_macro_sort_by_macro);
		zbx_vector_lld_macro_ptr_sort(&item->template_lld_macros, template_lld_macro_sort_by_macro);

		lld_macro_num = MAX(item->item_lld_macros.values_num, item->template_lld_macros.values_num);

		for (j = 0; j < lld_macro_num; j++)
		{
			if (j >= item->item_lld_macros.values_num)
			{
				plmpsrc = (zbx_template_lld_macro_t *)item->template_lld_macros.values[j];
				plmpdst = (zbx_template_lld_macro_t *)zbx_malloc(NULL,
						sizeof(zbx_template_lld_macro_t));
				plmpdst->lld_macro_pathid = 0;
				plmpdst->upd_flags = ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_RESET_FLAG;
				plmpdst->lld_macro = zbx_strdup(NULL, plmpsrc->lld_macro);
				plmpdst->path = zbx_strdup(NULL, plmpsrc->path);
				zbx_vector_lld_macro_ptr_append(&item->item_lld_macros, plmpdst);
				continue;
			}

			plmpdst = (zbx_template_lld_macro_t *)item->item_lld_macros.values[j];

			if (j >= item->template_lld_macros.values_num)
			{
				plmpdst->upd_flags |= ZBX_FLAG_TEMPLATE_LLD_MACRO_DELETE;
				continue;
			}

			plmpsrc = (zbx_template_lld_macro_t *)item->template_lld_macros.values[j];

			buffer = zbx_strdup(buffer, plmpsrc->lld_macro);

			if (0 != strcmp(plmpdst->lld_macro, buffer))
			{
				plmpdst->lld_macro_orig = zbx_strdup(NULL, plmpdst->lld_macro);
				zbx_free(plmpdst->lld_macro);

				plmpdst->lld_macro = buffer;
				buffer = NULL;
				plmpdst->upd_flags |= ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_LLD_MACRO;
			}

			buffer = zbx_strdup(buffer, plmpsrc->path);

			if (0 != strcmp(plmpdst->path, buffer))
			{
				plmpdst->path_orig = zbx_strdup(NULL, plmpdst->path);
				zbx_free(plmpdst->path);

				plmpdst->path = buffer;
				buffer = NULL;
				plmpdst->upd_flags |= ZBX_FLAG_TEMPLATE_LLD_MACRO_UPDATE_PATH;
			}
			else
				zbx_free(buffer);
		}
	}

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}
/******************************************************************************
 *                                                                            *
 * Purpose: prepare lld items by indexing them and scanning for already       *
 *          existing items                                                    *
 *                                                                            *
 * Parameters: items       - [IN] lld items                                   *
 *             lld_itemids - [OUT] identifiers of existing lld items          *
 *             lld_items   - [OUT] lld items indexed by itemid                *
 *                                                                            *
 ******************************************************************************/
static void	prepare_lld_items(const zbx_vector_ptr_t *items, zbx_vector_uint64_t *lld_itemids,
		zbx_hashset_t *lld_items)
{
	int				i;
	const zbx_template_item_t	*item;

	for (i = 0; i < items->values_num; i++)
	{
		item = (const zbx_template_item_t *)items->values[i];

		if (0 == (ZBX_FLAG_DISCOVERY_RULE & item->flags))
			continue;

		if (NULL == item->key)	/* item already existed */
			zbx_vector_uint64_append(lld_itemids, item->itemid);

		zbx_hashset_insert(lld_items, &item, sizeof(zbx_template_item_t *));
	}

	zbx_vector_uint64_sort(lld_itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
}

/******************************************************************************
 *                                                                            *
 * Purpose: copies template items to host                                     *
 *                                                                            *
 * Parameters:                                                                *
 *             hostid             - [IN]                                      *
 *             templateids        - [IN]                                      *
 *             audit_context_mode - [IN]                                      *
 *                                                                            *
 ******************************************************************************/
void	DBcopy_template_items(zbx_uint64_t hostid, const zbx_vector_uint64_t *templateids, int audit_context_mode)
{
	zbx_vector_ptr_t	items, lld_rules;
	int			new_conditions = 0;
	zbx_vector_uint64_t	lld_itemids;
	zbx_hashset_t		lld_items;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_vector_ptr_create(&items);
	zbx_vector_ptr_create(&lld_rules);

	get_template_items(hostid, templateids, &items);

	if (0 == items.values_num)
		goto out;

	get_template_lld_rule_map(&items, &lld_rules);

	new_conditions = calculate_template_lld_rule_conditionids(&lld_rules);
	update_template_lld_rule_formulas(&items, &lld_rules);

	link_template_dependent_items(&items);
	link_template_items_preproc(templateids, &items);
	link_template_items_tag(templateids, &items);
	link_template_items_param(templateids, &items);
	save_template_items(hostid, &items, audit_context_mode);
	save_template_lld_rules(&items, &lld_rules, new_conditions, audit_context_mode);
	save_template_discovery_prototypes(hostid, &items);
	copy_template_items_preproc(&items, audit_context_mode);
	copy_template_item_script_params(&items, audit_context_mode);
	copy_template_item_tags(&items, audit_context_mode);

	zbx_vector_uint64_create(&lld_itemids);
	zbx_hashset_create(&lld_items, (size_t)items.values_num, template_item_hash_func, template_item_compare_func);

	prepare_lld_items(&items, &lld_itemids, &lld_items);
	if (0 != lld_items.num_data)
	{
		link_template_lld_macro_paths(templateids, &lld_itemids, &lld_items, &items);
		copy_template_lld_macro_paths(&items, audit_context_mode);
		copy_template_lld_overrides(templateids, &lld_itemids, &lld_items, audit_context_mode);
	}

	zbx_hashset_destroy(&lld_items);
	zbx_vector_uint64_destroy(&lld_itemids);
out:
	zbx_vector_ptr_clear_ext(&lld_rules, (zbx_clean_func_t)free_lld_rule_map);
	zbx_vector_ptr_destroy(&lld_rules);

	zbx_vector_ptr_clear_ext(&items, (zbx_clean_func_t)free_template_item);
	zbx_vector_ptr_destroy(&items);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}