/*
** Copyright (C) 2001-2024 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 "dbupgrade.h"
#include "dbupgrade_macros.h"

#include "zbxeval.h"
#include "zbxregexp.h"
#include "zbxalgo.h"
#include "zbxjson.h"
#include "zbxsysinfo.h"
#include "zbxcrypto.h"
#include "zbxexpr.h"
#include "zbxnum.h"
#include "zbxparam.h"
#include "zbxdb.h"
#include "zbxdbhigh.h"
#include "zbxdbschema.h"
#include "zbxstr.h"
#include "zbxtime.h"
#include "zbxvariant.h"
#include "zbx_trigger_constants.h"
#include "zbx_scripts_constants.h"

/*
 * 5.4 development database patches
 */

#ifndef HAVE_SQLITE3

static int	DBpatch_5030000(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx='web.queue.config'"))
		return FAIL;

	return SUCCEED;
}

typedef struct
{
	zbx_uint64_t	id;
	zbx_uint64_t	userid;
	char		*idx;
	zbx_uint64_t	idx2;
	zbx_uint64_t	value_id;
	int		value_int;
	char		*value_str;
	char		*source;
	int		type;
}
zbx_dbpatch_profile_t;

static void	DBpatch_get_key_fields(zbx_db_row_t row, zbx_dbpatch_profile_t *profile, char **subsect, char **field,
		char **key)
{
	int	tok_idx = 0;
	char	*token;

	ZBX_DBROW2UINT64(profile->id, row[0]);
	ZBX_DBROW2UINT64(profile->userid, row[1]);
	profile->idx = zbx_strdup(profile->idx, row[2]);
	ZBX_DBROW2UINT64(profile->idx2, row[3]);
	ZBX_DBROW2UINT64(profile->value_id, row[4]);
	profile->value_int = atoi(row[5]);
	profile->value_str = zbx_strdup(profile->value_str, row[6]);
	profile->source = zbx_strdup(profile->source, row[7]);
	profile->type = atoi(row[8]);

	token = strtok(profile->idx, ".");

	while (NULL != token)
	{
		token = strtok(NULL, ".");
		tok_idx++;

		if (1 == tok_idx)
		{
			*subsect = zbx_strdup(*subsect, token);
		}
		else if (2 == tok_idx)
		{
			*key = zbx_strdup(*key, token);
		}
		else if (3 == tok_idx)
		{
			*field = zbx_strdup(*field, token);
			break;
		}
	}
}

static int	DBpatch_5030001(void)
{
	int		i, ret = SUCCEED;
	const char	*keys[] =
	{
		"web.items.php.sort",
		"web.items.php.sortorder",
		"web.triggers.php.sort",
		"web.triggers.php.sortorder",
		"web.graphs.php.sort",
		"web.graphs.php.sortorder",
		"web.host_discovery.php.sort",
		"web.host_discovery.php.sortorder",
		"web.httpconf.php.sort",
		"web.httpconf.php.sortorder",
		"web.disc_prototypes.php.sort",
		"web.disc_prototypes.php.sortorder",
		"web.trigger_prototypes.php.sort",
		"web.trigger_prototypes.php.sortorder",
		"web.host_prototypes.php.sort",
		"web.host_prototypes.php.sortorder"
	};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 0; SUCCEED == ret && i < (int)ARRSIZE(keys); i++)
	{
		char			*subsect = NULL, *field = NULL, *key = NULL;
		zbx_db_row_t		row;
		zbx_db_result_t		result;
		zbx_dbpatch_profile_t	profile = {0};

		result = zbx_db_select("select profileid,userid,idx,idx2,value_id,value_int,value_str,source,type"
				" from profiles where idx='%s'", keys[i]);

		if (NULL == (row = zbx_db_fetch(result)))
		{
			zbx_db_free_result(result);
			continue;
		}

		DBpatch_get_key_fields(row, &profile, &subsect, &field, &key);

		zbx_db_free_result(result);

		if (NULL == subsect || NULL == field || NULL == key)
		{
			zabbix_log(LOG_LEVEL_ERR, "failed to parse profile key fields for key '%s'", keys[i]);
			ret = FAIL;
		}

		if (SUCCEED == ret && ZBX_DB_OK > zbx_db_execute("insert into profiles "
				"(profileid,userid,idx,idx2,value_id,value_int,value_str,source,type) values "
				"(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'web.hosts.%s.%s.%s'," ZBX_FS_UI64 ","
				ZBX_FS_UI64 ",%d,'%s','%s',%d)",
				zbx_db_get_maxid("profiles"), profile.userid, subsect, key, field, profile.idx2, profile.value_id,
				profile.value_int, profile.value_str, profile.source, profile.type))
		{
			ret = FAIL;
		}

		if (SUCCEED == ret && ZBX_DB_OK > zbx_db_execute("insert into profiles "
				"(profileid,userid,idx,idx2,value_id,value_int,value_str,source,type) values "
				"(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'web.templates.%s.%s.%s'," ZBX_FS_UI64 ","
				ZBX_FS_UI64 ",%d,'%s','%s',%d)",
				zbx_db_get_maxid("profiles"), profile.userid, subsect, key, field, profile.idx2, profile.value_id,
				profile.value_int, profile.value_str, profile.source, profile.type))
		{
			ret = FAIL;
		}

		if (SUCCEED == ret &&
				ZBX_DB_OK > zbx_db_execute("delete from profiles where profileid=" ZBX_FS_UI64, profile.id))
		{
			ret = FAIL;
		}

		zbx_free(profile.idx);
		zbx_free(profile.value_str);
		zbx_free(profile.source);
		zbx_free(subsect);
		zbx_free(field);
		zbx_free(key);
	}

	return ret;
}

static int	DBpatch_5030002(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where "
			"idx like 'web.items.%%filter%%' or "
			"idx like 'web.triggers.%%filter%%' or "
			"idx like 'web.graphs.%%filter%%' or "
			"idx like 'web.host_discovery.%%filter%%' or "
			"idx like 'web.httpconf.%%filter%%' or "
			"idx like 'web.disc_prototypes.%%filter%%' or "
			"idx like 'web.trigger_prototypes.%%filter%%' or "
			"idx like 'web.host_prototypes.%%filter%%'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030003(void)
{
	int			ret = SUCCEED;
	char			*subsect = NULL, *field = NULL, *key = NULL;
	zbx_db_row_t		row;
	zbx_db_result_t		result;
	zbx_dbpatch_profile_t	profile = {0};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	result = zbx_db_select("select profileid,userid,idx,idx2,value_id,value_int,value_str,source,type"
			" from profiles where idx in ('web.dashbrd.list.sort','web.dashbrd.list.sortorder')");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		DBpatch_get_key_fields(row, &profile, &subsect, &field, &key);

		if (ZBX_DB_OK > zbx_db_execute("insert into profiles "
				"(profileid,userid,idx,idx2,value_id,value_int,value_str,source,type) values "
				"(" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'web.templates.%s.%s.%s'," ZBX_FS_UI64 ","
				ZBX_FS_UI64 ",%d,'%s','%s',%d)",
				zbx_db_get_maxid("profiles"), profile.userid, subsect, key, field, profile.idx2,
				profile.value_id, profile.value_int, profile.value_str, profile.source, profile.type))
		{
			ret = FAIL;
			break;
		}
	}

	zbx_db_free_result(result);

	zbx_free(profile.idx);
	zbx_free(profile.value_str);
	zbx_free(profile.source);
	zbx_free(subsect);
	zbx_free(field);
	zbx_free(key);

	return ret;
}

static int	DBpatch_5030004(void)
{
	const zbx_db_field_t	field = {"available", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("interface", &field);
}

static int	DBpatch_5030005(void)
{
	const zbx_db_field_t	field = {"error", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("interface", &field);
}

static int	DBpatch_5030006(void)
{
	const zbx_db_field_t	field = {"errors_from", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("interface", &field);
}

static int	DBpatch_5030007(void)
{
	const zbx_db_field_t	field = {"disable_until", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("interface", &field);
}

static int	DBpatch_5030008(void)
{
	return DBdrop_field("hosts", "available");
}

static int	DBpatch_5030009(void)
{
	return DBdrop_field("hosts", "ipmi_available");
}

static int	DBpatch_5030010(void)
{
	return DBdrop_field("hosts", "snmp_available");
}

static int	DBpatch_5030011(void)
{
	return DBdrop_field("hosts", "jmx_available");
}

static int	DBpatch_5030012(void)
{
	return DBdrop_field("hosts", "disable_until");
}

static int	DBpatch_5030013(void)
{
	return DBdrop_field("hosts", "ipmi_disable_until");
}

static int	DBpatch_5030014(void)
{
	return DBdrop_field("hosts", "snmp_disable_until");
}

static int	DBpatch_5030015(void)
{
	return DBdrop_field("hosts", "jmx_disable_until");
}

static int	DBpatch_5030016(void)
{
	return DBdrop_field("hosts", "errors_from");
}

static int	DBpatch_5030017(void)
{
	return DBdrop_field("hosts", "ipmi_errors_from");
}

static int	DBpatch_5030018(void)
{
	return DBdrop_field("hosts", "snmp_errors_from");
}

static int	DBpatch_5030019(void)
{
	return DBdrop_field("hosts", "jmx_errors_from");
}

static int	DBpatch_5030020(void)
{
	return DBdrop_field("hosts", "error");
}

static int	DBpatch_5030021(void)
{
	return DBdrop_field("hosts", "ipmi_error");
}

static int	DBpatch_5030022(void)
{
	return DBdrop_field("hosts", "snmp_error");
}

static int	DBpatch_5030023(void)
{
	return DBdrop_field("hosts", "jmx_error");
}

static int	DBpatch_5030024(void)
{
	return DBcreate_index("interface", "interface_3", "available", 0);
}

static int	DBpatch_5030025(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx='web.overview.type' or"
			" idx='web.actionconf.eventsource'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030026(void)
{
	const zbx_db_table_t	table =
			{"token", "tokenid", 0,
				{
					{"tokenid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"description", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0},
					{"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"token", NULL, NULL, NULL, 128, ZBX_TYPE_CHAR, 0, 0},
					{"lastaccess", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"status", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"expires_at", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"created_at", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"creator_userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030027(void)
{
	return DBcreate_index("token", "token_1", "name", 0);
}

static int	DBpatch_5030028(void)
{
	return DBcreate_index("token", "token_2", "userid,name", 1);
}

static int	DBpatch_5030029(void)
{
	return DBcreate_index("token", "token_3", "token", 1);
}

static int	DBpatch_5030030(void)
{
	return DBcreate_index("token", "token_4", "creator_userid", 0);
}

static int	DBpatch_5030031(void)
{
	const zbx_db_field_t	field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("token", 1, &field);
}

static int	DBpatch_5030032(void)
{
	const zbx_db_field_t	field = {"creator_userid", NULL, "users", "userid", 0, 0, 0, 0};

	return DBadd_foreign_key("token", 2, &field);
}

static int	DBpatch_5030033(void)
{
	const zbx_db_field_t	field = {"timeout", "30s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030034(void)
{
	const zbx_db_field_t	old_field = {"command", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"command", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("scripts", &field, &old_field);
}

static int	DBpatch_5030035(void)
{
	const zbx_db_table_t	table =
			{"script_param", "script_paramid", 0,
				{
					{"script_paramid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"scriptid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030036(void)
{
	const zbx_db_field_t	field = {"scriptid", NULL, "scripts", "scriptid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("script_param", 1, &field);
}

static int	DBpatch_5030037(void)
{
	return DBcreate_index("script_param", "script_param_1", "scriptid,name", 1);
}

static int	DBpatch_5030038(void)
{
	const zbx_db_field_t	field = {"type", "5", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBset_default("scripts", &field);
}

static int	DBpatch_5030039(void)
{
	const zbx_db_table_t	table =
			{"valuemap", "valuemapid", 0,
				{
					{"valuemapid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"hostid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030040(void)
{
	return DBcreate_index("valuemap", "valuemap_1", "hostid,name", 1);
}

static int	DBpatch_5030041(void)
{
	const zbx_db_field_t	field = {"hostid", NULL, "hosts", "hostid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("valuemap", 1, &field);
}

static int	DBpatch_5030042(void)
{
	const zbx_db_table_t	table =
			{"valuemap_mapping", "valuemap_mappingid", 0,
				{
					{"valuemap_mappingid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"valuemapid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"newvalue", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030043(void)
{
	return DBcreate_index("valuemap_mapping", "valuemap_mapping_1", "valuemapid,value", 1);
}

static int	DBpatch_5030044(void)
{
	const zbx_db_field_t	field = {"valuemapid", NULL, "valuemap", "valuemapid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("valuemap_mapping", 1, &field);
}

static int	DBpatch_5030045(void)
{
	return DBdrop_foreign_key("items", 3);
}

typedef struct
{
	zbx_uint64_t		valuemapid;
	char			*name;
	zbx_vector_ptr_pair_t	mappings;
}
zbx_valuemap_t;

typedef struct
{
	zbx_uint64_t		hostid;
	zbx_uint64_t		valuemapid;
	zbx_vector_uint64_t	itemids;
}
zbx_host_t;

static int	host_compare_func(const void *d1, const void *d2)
{
	const zbx_host_t	*h1 = *(const zbx_host_t * const *)d1;
	const zbx_host_t	*h2 = *(const zbx_host_t * const *)d2;

	ZBX_RETURN_IF_NOT_EQUAL(h1->hostid, h2->hostid);
	ZBX_RETURN_IF_NOT_EQUAL(h1->valuemapid, h2->valuemapid);

	return 0;
}

static void	get_discovered_itemids(const zbx_vector_uint64_t *itemids, zbx_vector_uint64_t *discovered_itemids)
{
	char		*sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_result_t	result;
	zbx_db_row_t	row;

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select itemid from item_discovery where");
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "parent_itemid", itemids->values, itemids->values_num);

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

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_uint64_t	itemid;

		ZBX_STR2UINT64(itemid, row[0]);

		zbx_vector_uint64_append(discovered_itemids, itemid);
	}
	zbx_db_free_result(result);
}

static void	get_template_itemids_by_templateids(zbx_vector_uint64_t *templateids, zbx_vector_uint64_t *itemids,
		zbx_vector_uint64_t *discovered_itemids)
{
	zbx_db_result_t		result;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t		row;
	zbx_vector_uint64_t	templateids_tmp;

	zbx_vector_uint64_create(&templateids_tmp);
	zbx_vector_uint64_append_array(&templateids_tmp, templateids->values, templateids->values_num);

	zbx_vector_uint64_clear(templateids);

	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select i1.itemid"
			" from items i1"
				" where exists ("
					"select null"
					" from items i2"
					" where i2.templateid=i1.itemid"
				")"
				" and");
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i1.templateid", templateids_tmp.values,
			templateids_tmp.values_num);

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

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_uint64_t	itemid;

		ZBX_STR2UINT64(itemid, row[0]);

		zbx_vector_uint64_append(templateids, itemid);
	}
	zbx_db_free_result(result);

	sql_offset = 0;
	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "select i2.itemid"
			" from items i1,item_discovery i2"
			" where i2.parent_itemid=i1.itemid"
			" and");
	zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "i1.templateid", templateids_tmp.values,
			templateids_tmp.values_num);

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

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_uint64_t	itemid;

		ZBX_STR2UINT64(itemid, row[0]);

		zbx_vector_uint64_append(discovered_itemids, itemid);
	}
	zbx_db_free_result(result);

	zbx_vector_uint64_destroy(&templateids_tmp);

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

	zbx_vector_uint64_append_array(itemids, templateids->values, templateids->values_num);

	get_template_itemids_by_templateids(templateids, itemids, discovered_itemids);
}

static void	host_free(zbx_host_t *host)
{
	zbx_vector_uint64_destroy(&host->itemids);
	zbx_free(host);
}

static int	DBpatch_5030046(void)
{
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	int			i, j, ret = SUCCEED;
	zbx_hashset_t		valuemaps;
	zbx_hashset_iter_t	iter;
	zbx_valuemap_t		valuemap_local, *valuemap;
	zbx_uint64_t		valuemapid;
	zbx_vector_ptr_t	hosts;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_vector_uint64_t	templateids, discovered_itemids;
	zbx_db_insert_t		db_insert_valuemap, db_insert_valuemap_mapping;

	zbx_hashset_create(&valuemaps, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

	zbx_vector_ptr_create(&hosts);
	zbx_vector_ptr_reserve(&hosts, 1000);

	zbx_vector_uint64_create(&templateids);
	zbx_vector_uint64_reserve(&templateids, 1000);

	zbx_vector_uint64_create(&discovered_itemids);
	zbx_vector_uint64_reserve(&discovered_itemids, 1000);

	result = zbx_db_select(
			"select m.valuemapid,v.name,m.value,m.newvalue"
			" from valuemaps v"
			" left join mappings m on v.valuemapid=m.valuemapid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_ptr_pair_t	pair;

		if (SUCCEED == zbx_db_is_null(row[0]))
		{
			zabbix_log(LOG_LEVEL_WARNING, "empty valuemap '%s' was removed", row[1]);
			continue;
		}

		ZBX_STR2UINT64(valuemap_local.valuemapid, row[0]);

		if (NULL == (valuemap = (zbx_valuemap_t *)zbx_hashset_search(&valuemaps, &valuemap_local)))
		{
			valuemap = zbx_hashset_insert(&valuemaps, &valuemap_local, sizeof(valuemap_local));
			valuemap->name = zbx_strdup(NULL, row[1]);
			zbx_vector_ptr_pair_create(&valuemap->mappings);
		}

		pair.first = zbx_strdup(NULL, row[2]);
		pair.second = zbx_strdup(NULL, row[3]);

		zbx_vector_ptr_pair_append(&valuemap->mappings, pair);
	}
	zbx_db_free_result(result);

	result = zbx_db_select("select h.flags,i.hostid,i.valuemapid,i.itemid"
			" from items i,hosts h"
			" where i.templateid is null"
				" and i.valuemapid is not null"
				" and i.flags in (0,2)"
				" and h.hostid=i.hostid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_host_t	host_local, *host;
		zbx_uint64_t	itemid;
		unsigned char	flags;

		ZBX_STR2UCHAR(flags, row[0]);
		ZBX_STR2UINT64(host_local.hostid, row[1]);

		if (ZBX_FLAG_DISCOVERY_CREATED == flags)
			host_local.valuemapid = 0;
		else
			ZBX_STR2UINT64(host_local.valuemapid, row[2]);

		ZBX_STR2UINT64(itemid, row[3]);

		if (FAIL == (i = zbx_vector_ptr_search(&hosts, &host_local, host_compare_func)))
		{
			host = zbx_malloc(NULL, sizeof(zbx_host_t));
			host->hostid = host_local.hostid;
			host->valuemapid = host_local.valuemapid;
			zbx_vector_uint64_create(&host->itemids);

			zbx_vector_ptr_append(&hosts, host);
		}
		else
			host = (zbx_host_t *)hosts.values[i];

		zbx_vector_uint64_append(&host->itemids, itemid);
	}
	zbx_db_free_result(result);

	zbx_db_insert_prepare(&db_insert_valuemap, "valuemap", "valuemapid", "hostid", "name", (char *)NULL);
	zbx_db_insert_prepare(&db_insert_valuemap_mapping, "valuemap_mapping", "valuemap_mappingid",
			"valuemapid", "value", "newvalue", (char *)NULL);

	for (i = 0, valuemapid = 0; i < hosts.values_num; i++)
	{
		zbx_host_t	*host;

		host = (zbx_host_t *)hosts.values[i];

		if (NULL != (valuemap = (zbx_valuemap_t *)zbx_hashset_search(&valuemaps, &host->valuemapid)))
		{
			zbx_db_insert_add_values(&db_insert_valuemap, ++valuemapid, host->hostid, valuemap->name);

			for (j = 0; j < valuemap->mappings.values_num; j++)
			{
				zbx_db_insert_add_values(&db_insert_valuemap_mapping, __UINT64_C(0), valuemapid,
						valuemap->mappings.values[j].first,
						valuemap->mappings.values[j].second);
			}
		}
	}

	zbx_db_insert_execute(&db_insert_valuemap);
	zbx_db_insert_clean(&db_insert_valuemap);

	zbx_db_insert_autoincrement(&db_insert_valuemap_mapping, "valuemap_mappingid");
	zbx_db_insert_execute(&db_insert_valuemap_mapping);
	zbx_db_insert_clean(&db_insert_valuemap_mapping);

	for (i = 0, valuemapid = 0; i < hosts.values_num; i++)
	{
		zbx_host_t	*host;
		char		buffer[MAX_STRING_LEN];

		host = (zbx_host_t *)hosts.values[i];

		if (NULL != zbx_hashset_search(&valuemaps, &host->valuemapid))
		{
			zbx_snprintf(buffer, sizeof(buffer), "update items set valuemapid=" ZBX_FS_UI64 " where",
					++valuemapid);
		}
		else
			zbx_strlcpy(buffer, "update items set valuemapid=null where", sizeof(buffer));

		/* update valuemapid for top level items on a template/host */
		zbx_vector_uint64_sort(&host->itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
		zbx_db_prepare_multiple_query(buffer, "itemid", &host->itemids, &sql, &sql_alloc, &sql_offset);
		/* get discovered itemids for not templated item prototypes on a host */
		get_discovered_itemids(&host->itemids, &discovered_itemids);

		zbx_vector_uint64_append_array(&templateids, host->itemids.values, host->itemids.values_num);
		get_template_itemids_by_templateids(&templateids, &host->itemids, &discovered_itemids);

		/* make sure if multiple hosts are linked to same not nested template then there is only */
		/* update by templateid from template and no selection by numerous itemids               */
		zbx_vector_uint64_sort(&host->itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
		zbx_db_prepare_multiple_query(buffer, "templateid", &host->itemids, &sql, &sql_alloc, &sql_offset);

		if (0 != discovered_itemids.values_num)
		{
			zbx_vector_uint64_sort(&discovered_itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
			zbx_db_prepare_multiple_query(buffer, "itemid", &discovered_itemids, &sql, &sql_alloc, &sql_offset);
			zbx_vector_uint64_clear(&discovered_itemids);
		}
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;

	zbx_free(sql);

	zbx_hashset_iter_reset(&valuemaps, &iter);
	while (NULL != (valuemap = (zbx_valuemap_t *)zbx_hashset_iter_next(&iter)))
	{
		zbx_free(valuemap->name);

		for (i = 0; i < valuemap->mappings.values_num; i++)
		{
			zbx_free(valuemap->mappings.values[i].first);
			zbx_free(valuemap->mappings.values[i].second);
		}
		zbx_vector_ptr_pair_destroy(&valuemap->mappings);
	}

	zbx_vector_ptr_clear_ext(&hosts, (zbx_clean_func_t)host_free);
	zbx_vector_ptr_destroy(&hosts);
	zbx_hashset_destroy(&valuemaps);

	zbx_vector_uint64_destroy(&templateids);
	zbx_vector_uint64_destroy(&discovered_itemids);

	return ret;
}

static int	DBpatch_5030047(void)
{
	const zbx_db_field_t	field = {"valuemapid", NULL, "valuemap", "valuemapid", 0, ZBX_TYPE_ID, 0, 0};

	return DBadd_foreign_key("items", 3, &field);
}

static int	DBpatch_5030048(void)
{
	return DBdrop_table("mappings");
}

static int	DBpatch_5030049(void)
{
	return DBdrop_table("valuemaps");
}

static int	DBpatch_5030050(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where"
			" idx in ('web.valuemap.list.sort', 'web.valuemap.list.sortorder')"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030051(void)
{
	return DBdrop_field("config", "compression_availability");
}

static int	DBpatch_5030052(void)
{
	return DBdrop_index("users", "users_1");
}

static int	DBpatch_5030053(void)
{
	const zbx_db_field_t	field = {"username", "", NULL, NULL, 100, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBrename_field("users", "alias", &field);
}

static int	DBpatch_5030054(void)
{
	return DBcreate_index("users", "users_1", "username", 1);
}

static int	DBpatch_5030055(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update profiles set idx='web.user.filter_username'"
			" where idx='web.user.filter_alias'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030056(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update profiles set value_str='username'"
			" where idx='web.user.sort' and value_str like 'alias'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

/* Patches and helper functions for ZBXNEXT-6368 */

static int	is_valid_opcommand_type(const char *type_str, const char *scriptid)
{
#define ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT	4	/* not used after upgrade */
	unsigned int	type;

	if (SUCCEED != zbx_is_uint31(type_str, &type))
		return FAIL;

	switch (type)
	{
		case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
		case ZBX_SCRIPT_TYPE_IPMI:
		case ZBX_SCRIPT_TYPE_SSH:
		case ZBX_SCRIPT_TYPE_TELNET:
			if (SUCCEED == zbx_db_is_null(scriptid))
				return SUCCEED;
			else
				return FAIL;
		case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
			if (FAIL == zbx_db_is_null(scriptid))
				return SUCCEED;
			else
				return FAIL;
		default:
			return FAIL;
	}
#undef ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT
}

static int	validate_types_in_opcommand(void)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	if (NULL == (result = zbx_db_select("select operationid,type,scriptid from opcommand")))
	{
		zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'opcommand'", __func__);
		return FAIL;
	}

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (SUCCEED != is_valid_opcommand_type(row[1], row[2]))
		{
			zabbix_log(LOG_LEVEL_CRIT, "%s(): invalid record in table \"opcommand\": operationid: %s"
					" type: %s scriptid: %s", __func__, row[0], row[1],
					(SUCCEED == zbx_db_is_null(row[2])) ? "value is NULL" : row[2]);
			ret = FAIL;
			break;
		}
	}

	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_5030057(void)
{
	return validate_types_in_opcommand();
}

static int	DBpatch_5030058(void)
{
	const zbx_db_field_t	field = {"scope", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030059(void)
{
	const zbx_db_field_t	field = {"port", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030060(void)
{
	const zbx_db_field_t	field = {"authtype", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030061(void)
{
	const zbx_db_field_t	field = {"username", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030062(void)
{
	const zbx_db_field_t	field = {"password", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030063(void)
{
	const zbx_db_field_t	field = {"publickey", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030064(void)
{
	const zbx_db_field_t	field = {"privatekey", "", NULL, NULL, 64, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

static int	DBpatch_5030065(void)
{
	const zbx_db_field_t	field = {"menu_path", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("scripts", &field);
}

/******************************************************************************
 *                                                                            *
 * Purpose: set value for 'scripts' table column 'scope' for existing global  *
 *          scripts                                                           *
 *                                                                            *
 * Return value: SUCCEED or FAIL                                              *
 *                                                                            *
 * Comments: 'scope' is set only for scripts which are NOT used in any action *
 *           operation. Otherwise the 'scope' default value is used, no need  *
 *           to modify it.                                                    *
 *                                                                            *
 ******************************************************************************/
static int	DBpatch_5030066(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update scripts set scope=%d"
			" where scriptid not in ("
			"select distinct scriptid"
			" from opcommand"
			" where scriptid is not null)", ZBX_SCRIPT_SCOPE_HOST))
	{
		return FAIL;
	}

	return SUCCEED;
}

static char	*zbx_rename_host_macros(const char *command)
{
	char	*p1, *p2, *p3, *p4, *p5, *p6, *p7;

	p1 = zbx_string_replace(command, "{HOST.CONN}", "{HOST.TARGET.CONN}");
	p2 = zbx_string_replace(p1, "{HOST.DNS}", "{HOST.TARGET.DNS}");
	p3 = zbx_string_replace(p2, "{HOST.HOST}", "{HOST.TARGET.HOST}");
	p4 = zbx_string_replace(p3, "{HOST.IP}", "{HOST.TARGET.IP}");
	p5 = zbx_string_replace(p4, "{HOST.NAME}", "{HOST.TARGET.NAME}");
	p6 = zbx_string_replace(p5, "{HOSTNAME}", "{HOST.TARGET.NAME}");
	p7 = zbx_string_replace(p6, "{IPADDRESS}", "{HOST.TARGET.IP}");

	zbx_free(p1);
	zbx_free(p2);
	zbx_free(p3);
	zbx_free(p4);
	zbx_free(p5);
	zbx_free(p6);

	return p7;
}

/******************************************************************************
 *                                                                            *
 * Purpose: rename some {HOST.*} macros to {HOST.TARGET.*} in existing global *
 *          scripts which are used in actions                                 *
 *                                                                            *
 * Return value: SUCCEED or FAIL                                              *
 *                                                                            *
 ******************************************************************************/
static int	DBpatch_5030067(void)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	if (NULL == (result = zbx_db_select("select scriptid,command"
			" from scripts"
			" where scriptid in (select distinct scriptid from opcommand where scriptid is not null)")))
	{
		zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
		return FAIL;
	}

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char	*command, *command_esc;
		int	rc;

		command_esc = zbx_db_dyn_escape_field("scripts", "command", (command = zbx_rename_host_macros(row[1])));

		zbx_free(command);

		rc = zbx_db_execute("update scripts set command='%s' where scriptid=%s", command_esc, row[0]);

		zbx_free(command_esc);

		if (ZBX_DB_OK > rc)
		{
			ret = FAIL;
			break;
		}
	}
	zbx_db_free_result(result);

	return ret;
}

/******************************************************************************
 *                                                                            *
 * Purpose: helper function to split script name into menu_path and name      *
 *                                                                            *
 * Parameters:                                                                *
 *                name - [IN] old name                                        *
 *           menu_path - [OUT] menu path part, must be deallocated by caller  *
 *   name_without_path - [OUT] name, DO NOT deallocate in caller              *
 *                                                                            *
 ******************************************************************************/
static void	zbx_split_name(const char *name, char **menu_path, const char **name_without_path)
{
	char	*p;

	if (NULL == (p = strrchr(name, '/')))
		return;

	/* do not split if '/' is found at the beginning or at the end */
	if (name == p || '\0' == *(p + 1))
		return;

	*menu_path = zbx_strdup(*menu_path, name);

	p = *menu_path + (p - name);
	*p = '\0';
	*name_without_path = p + 1;
}

/******************************************************************************
 *                                                                            *
 * Purpose: helper function to assist in making unique script names           *
 *                                                                            *
 * Parameters:                                                                *
 *            name - [IN] proposed name, to be tried first                    *
 *          suffix - [IN/OUT] numeric suffix to start from                    *
 *     unique_name - [OUT] unique name, must be deallocated by caller         *
 *                                                                            *
 * Return value: SUCCEED - unique name found, FAIL - DB error                 *
 *                                                                            *
 * Comments: pass initial suffix=0 to get "script ABC", "script ABC 2",       *
 *           "script ABC 3", ... .                                            *
 *           Pass initial suffix=1 to get "script ABC 1", "script ABC 2",     *
 *           "script ABC 3", ... .                                            *
 *                                                                            *
 ******************************************************************************/
static int	zbx_make_script_name_unique(const char *name, int *suffix, char **unique_name)
{
	zbx_db_result_t	result;
	char		*sql, *try_name = NULL, *try_name_esc = NULL;

	while (1)
	{
		if (0 == *suffix)
		{
			try_name = zbx_strdup(NULL, name);
			(*suffix)++;
		}
		else
			try_name = zbx_dsprintf(try_name, "%s %d", name, *suffix);

		(*suffix)++;

		try_name_esc = zbx_db_dyn_escape_string(try_name);

		sql = zbx_dsprintf(NULL, "select scriptid from scripts where name='%s'", try_name_esc);

		zbx_free(try_name_esc);

		if (NULL == (result = zbx_db_select_n(sql, 1)))
		{
			zbx_free(try_name);
			zbx_free(sql);
			zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
			return FAIL;
		}

		zbx_free(sql);

		if (NULL == zbx_db_fetch(result))
		{
			*unique_name = try_name;
			zbx_db_free_result(result);
			return SUCCEED;
		}

		zbx_db_free_result(result);
	}
}

/******************************************************************************
 *                                                                            *
 * Purpose: split script name between 'menu_path' and 'name' columns for      *
 *          existing global scripts                                           *
 *                                                                            *
 * Return value: SUCCEED or FAIL                                              *
 *                                                                            *
 ******************************************************************************/
static int	DBpatch_5030068(void)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	if (NULL == (result = zbx_db_select("select scriptid,name"
			" from scripts")))
	{
		zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'scripts'", __func__);
		return FAIL;
	}

	while (NULL != (row = zbx_db_fetch(result)))
	{
		const char	*scriptid = row[0];
		const char	*name = row[1];
		const char	*name_without_path;
		char		*menu_path = NULL, *menu_path_esc = NULL;
		char		*name_without_path_unique = NULL, *name_esc = NULL;
		int		rc, suffix = 0;

		zbx_split_name(name, &menu_path, &name_without_path);

		if (NULL == menu_path)
			continue;

		if (SUCCEED != zbx_make_script_name_unique(name_without_path, &suffix, &name_without_path_unique))
		{
			zbx_free(menu_path);
			ret = FAIL;
			break;
		}

		menu_path_esc = zbx_db_dyn_escape_string(menu_path);
		name_esc = zbx_db_dyn_escape_string(name_without_path_unique);

		rc = zbx_db_execute("update scripts set menu_path='%s',name='%s' where scriptid=%s",
				menu_path_esc, name_esc, scriptid);

		zbx_free(name_esc);
		zbx_free(menu_path_esc);
		zbx_free(name_without_path_unique);
		zbx_free(menu_path);

		if (ZBX_DB_OK > rc)
		{
			ret = FAIL;
			break;
		}
	}

	zbx_db_free_result(result);

	return ret;
}

typedef struct
{
	char	*command;
	char	*username;
	char	*password;
	char	*publickey;
	char	*privatekey;
	char	*type;
	char	*execute_on;
	char	*port;
	char	*authtype;
}
zbx_opcommand_parts_t;

typedef struct
{
	size_t		size;
	char		*record;
	zbx_uint64_t	scriptid;
}
zbx_opcommand_rec_t;

ZBX_VECTOR_DECL(opcommands, zbx_opcommand_rec_t)
ZBX_VECTOR_IMPL(opcommands, zbx_opcommand_rec_t)

/******************************************************************************
 *                                                                            *
 * Purpose: helper function, packs parts of remote command into one memory    *
 *          chunk for efficient storing and comparing                         *
 *                                                                            *
 * Parameters:                                                                *
 *           parts - [IN] structure with all remote command components        *
 *   packed_record - [OUT] memory chunk with packed data. Must be deallocated *
 *                   by caller.                                               *
 *                                                                            *
 * Return value: size of memory chunk with the packed remote command          *
 *                                                                            *
 ******************************************************************************/
static size_t	zbx_pack_record(const zbx_opcommand_parts_t *parts, char **packed_record)
{
	size_t	size;
	char	*p, *p_end;

	size = strlen(parts->command) + strlen(parts->username) + strlen(parts->password) + strlen(parts->publickey) +
			strlen(parts->privatekey) + strlen(parts->type) + strlen(parts->execute_on) +
			strlen(parts->port) + strlen(parts->authtype) + 9; /* 9 terminating '\0' bytes for 9 parts */

	*packed_record = (char *)zbx_malloc(*packed_record, size);
	p = *packed_record;
	p_end = *packed_record + size;

	p += zbx_strlcpy(p, parts->command, size) + 1;
	p += zbx_strlcpy(p, parts->username, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->password, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->publickey, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->privatekey, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->type, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->execute_on, (size_t)(p_end - p)) + 1;
	p += zbx_strlcpy(p, parts->port, (size_t)(p_end - p)) + 1;
	zbx_strlcpy(p, parts->authtype, (size_t)(p_end - p));

	return size;
}

/******************************************************************************
 *                                                                            *
 * Purpose: checking if this remote command is a new one or a duplicate one   *
 *          and storing the assigned new global script id                     *
 *                                                                            *
 * Parameters:                                                                *
 *      opcommands - [IN] vector used for checking duplicates                 *
 *           parts - [IN] structure with all remote command components        *
 *           index - [OUT] index of vector element used to store information  *
 *                   about the remote command (either a new one or            *
 *                   an existing one)                                         *
 *                                                                            *
 * Return value: IS_NEW for new elements, IS_DUPLICATE for elements already   *
 *               seen                                                         *
 *                                                                            *
 ******************************************************************************/
#define IS_NEW		0
#define IS_DUPLICATE	1

static int	zbx_check_duplicate(zbx_vector_opcommands_t *opcommands,
		const zbx_opcommand_parts_t *parts, int *index)
{
	char			*packed_record = NULL;
	size_t			size;
	zbx_opcommand_rec_t	elem;
	int			i;

	size = zbx_pack_record(parts, &packed_record);

	for (i = 0; i < opcommands->values_num; i++)
	{
		if (size == opcommands->values[i].size &&
				0 == memcmp(opcommands->values[i].record, packed_record, size))
		{
			zbx_free(packed_record);
			*index = i;
			return IS_DUPLICATE;
		}
	}

	elem.size = size;
	elem.record = packed_record;
	elem.scriptid = 0;
	zbx_vector_opcommands_append(opcommands, elem);
	*index = opcommands->values_num - 1;

	return IS_NEW;
}

/******************************************************************************
 *                                                                            *
 * Purpose: migrate remote commands from table 'opcommand' to table 'scripts' *
 *          and convert them into global scripts                              *
 *                                                                            *
 ******************************************************************************/
static int	DBpatch_5030069(void)
{
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	int			ret = SUCCEED, i, suffix = 1;
	zbx_vector_opcommands_t	opcommands;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	zbx_vector_opcommands_create(&opcommands);

	if (NULL == (result = zbx_db_select("select command,username,password,publickey,privatekey,type,execute_on,port,"
			"authtype,operationid"
			" from opcommand"
			" where scriptid is null"
			" order by command,username,password,publickey,privatekey,type,execute_on,port,authtype")))
	{
		zabbix_log(LOG_LEVEL_CRIT, "%s(): cannot select from table 'opcommand'", __func__);
		zbx_vector_opcommands_destroy(&opcommands);

		return FAIL;
	}

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char			*operationid;
		int			index;
		zbx_opcommand_parts_t	parts;

		parts.command = row[0];
		parts.username = row[1];
		parts.password = row[2];
		parts.publickey = row[3];
		parts.privatekey = row[4];
		parts.type = row[5];
		parts.execute_on = row[6];
		parts.port = row[7];
		parts.authtype = row[8];
		operationid = row[9];

		if (IS_NEW == zbx_check_duplicate(&opcommands, &parts, &index))
		{
			char		*script_name = NULL, *script_name_esc;
			char		*command_esc, *port_esc, *username_esc;
			char		*password_esc, *publickey_esc, *privatekey_esc;
			zbx_uint64_t	scriptid, type, execute_on, authtype, operationid_num;
			int		rc;

			if (SUCCEED != zbx_make_script_name_unique("Script", &suffix, &script_name))
			{
				ret = FAIL;
				break;
			}

			scriptid = zbx_db_get_maxid("scripts");

			ZBX_DBROW2UINT64(type, parts.type);
			ZBX_DBROW2UINT64(execute_on, parts.execute_on);
			ZBX_DBROW2UINT64(authtype, parts.authtype);
			ZBX_DBROW2UINT64(operationid_num, operationid);

			script_name_esc = zbx_db_dyn_escape_string(script_name);
			command_esc = zbx_db_dyn_escape_string(parts.command);
			port_esc = zbx_db_dyn_escape_string(parts.port);
			username_esc = zbx_db_dyn_escape_string(parts.username);
			password_esc = zbx_db_dyn_escape_string(parts.password);
			publickey_esc = zbx_db_dyn_escape_string(parts.publickey);
			privatekey_esc = zbx_db_dyn_escape_string(parts.privatekey);

			zbx_free(script_name);

			rc = zbx_db_execute("insert into scripts (scriptid,name,command,description,type,execute_on,scope,"
					"port,authtype,username,password,publickey,privatekey) values ("
					ZBX_FS_UI64 ",'%s','%s',''," ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,'%s',"
					ZBX_FS_UI64 ",'%s','%s','%s','%s')",
					scriptid, script_name_esc, command_esc, type, execute_on,
					ZBX_SCRIPT_SCOPE_ACTION, port_esc, authtype,
					username_esc, password_esc, publickey_esc, privatekey_esc);

			zbx_free(privatekey_esc);
			zbx_free(publickey_esc);
			zbx_free(password_esc);
			zbx_free(username_esc);
			zbx_free(port_esc);
			zbx_free(command_esc);
			zbx_free(script_name_esc);

			if (ZBX_DB_OK > rc || ZBX_DB_OK > zbx_db_execute("update opcommand set scriptid=" ZBX_FS_UI64
						" where operationid=" ZBX_FS_UI64, scriptid, operationid_num))
			{
				ret = FAIL;
				break;
			}

			opcommands.values[index].scriptid = scriptid;
		}
		else	/* IS_DUPLICATE */
		{
			zbx_uint64_t	scriptid;

			/* link to a previously migrated script */
			scriptid = opcommands.values[index].scriptid;

			if (ZBX_DB_OK > zbx_db_execute("update opcommand set scriptid=" ZBX_FS_UI64
					" where operationid=%s", scriptid, operationid))
			{
				ret = FAIL;
				break;
			}
		}
	}

	zbx_db_free_result(result);

	for (i = 0; i < opcommands.values_num; i++)
		zbx_free(opcommands.values[i].record);

	zbx_vector_opcommands_destroy(&opcommands);

	return ret;
}
#undef IS_NEW
#undef IS_DUPLICATE

static int	DBpatch_5030070(void)
{
	const zbx_db_field_t	field = {"scriptid", NULL, "scripts","scriptid", 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0};

	return DBset_not_null("opcommand", &field);
}

static int	DBpatch_5030071(void)
{
	return DBdrop_field("opcommand", "execute_on");
}

static int	DBpatch_5030072(void)
{
	return DBdrop_field("opcommand", "port");
}

static int	DBpatch_5030073(void)
{
	return DBdrop_field("opcommand", "authtype");
}

static int	DBpatch_5030074(void)
{
	return DBdrop_field("opcommand", "username");
}

static int	DBpatch_5030075(void)
{
	return DBdrop_field("opcommand", "password");
}

static int	DBpatch_5030076(void)
{
	return DBdrop_field("opcommand", "publickey");
}

static int	DBpatch_5030077(void)
{
	return DBdrop_field("opcommand", "privatekey");
}

static int	DBpatch_5030078(void)
{
	return DBdrop_field("opcommand", "command");
}

static int	DBpatch_5030079(void)
{
	return DBdrop_field("opcommand", "type");
}

static int	DBpatch_5030080(void)
{
	const zbx_db_field_t	old_field = {"command", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"command", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("task_remote_command", &field, &old_field);
}
/*  end of ZBXNEXT-6368 patches */

static int	DBpatch_5030081(void)
{
	const zbx_db_field_t	field = {"display_period", "30", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("dashboard", &field);
}

static int	DBpatch_5030082(void)
{
	const zbx_db_field_t	field = {"auto_start", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("dashboard", &field);
}

static int	DBpatch_5030083(void)
{
	const zbx_db_table_t	table =
			{"dashboard_page", "dashboard_pageid", 0,
				{
					{"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"dashboardid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"display_period", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"sortorder", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030084(void)
{
	const zbx_db_field_t	field = {"dashboardid", NULL, "dashboard", "dashboardid", 0, 0, 0,
			ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("dashboard_page", 1, &field);
}

static int	DBpatch_5030085(void)
{
	return DBcreate_index("dashboard_page", "dashboard_page_1", "dashboardid", 0);
}

static int	DBpatch_5030086(void)
{
	if (0 == (ZBX_PROGRAM_TYPE_SERVER & DBget_program_type()))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute(
			"insert into dashboard_page (dashboard_pageid,dashboardid)"
			" select dashboardid,dashboardid from dashboard"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030087(void)
{
	const zbx_db_field_t	field = {"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0};

	if (SUCCEED != DBadd_field("widget", &field))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030088(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update widget set dashboard_pageid=dashboardid"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030089(void)
{
	const zbx_db_field_t	field = {"dashboard_pageid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0};

	return DBset_not_null("widget", &field);
}

static int	DBpatch_5030090(void)
{
	return DBdrop_foreign_key("widget", 1);
}

static int	DBpatch_5030091(void)
{
	return DBdrop_field("widget", "dashboardid");
}

static int	DBpatch_5030092(void)
{
	return DBcreate_index("widget", "widget_1", "dashboard_pageid", 0);
}

static int	DBpatch_5030093(void)
{
	const zbx_db_field_t	field = {"dashboard_pageid", NULL, "dashboard_page", "dashboard_pageid", 0, 0, 0,
			ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("widget", 1, &field);
}

typedef struct
{
	uint64_t	screenitemid;
	uint64_t	screenid;
	int		resourcetype;
	uint64_t	resourceid;
	int		width;
	int		height;
	int		x;
	int		y;
	int		colspan;
	int		rowspan;
	int		elements;
	int		style;
	char		*url;
	int		sort_triggers;
	char		*application;
	int		dynamic;
}
zbx_db_screen_item_t;

typedef struct
{
	uint64_t	widget_fieldid;
	int		type;
	char		*name;
	int		value_int;
	char		*value_str;
	uint64_t	value_itemid;
	uint64_t	value_graphid;
	uint64_t	value_groupid;
	uint64_t	value_hostid;
	uint64_t	value_sysmapid;
}
zbx_db_widget_field_t;

typedef struct
{
	int	position;
	int	span;
	int	size;
}
zbx_screen_item_dim_t;

typedef struct
{
	uint64_t	dashboardid;
	char		*name;
	uint64_t	userid;
	int		private;
	int		display_period;
}
zbx_db_dashboard_t;

typedef struct
{
	uint64_t	dashboard_pageid;
	uint64_t	dashboardid;
	char		*name;
}
zbx_db_dashboard_page_t;

typedef struct
{
	uint64_t	widgetid;
	uint64_t	dashboardid;
	char		*type;
	char		*name;
	int		x;
	int		y;
	int		width;
	int		height;
	int		view_mode;
}
zbx_db_widget_t;

#define DASHBOARD_MAX_COLS			(24)
#define DASHBOARD_MAX_ROWS			(64)
#define DASHBOARD_WIDGET_MIN_ROWS		(2)
#define DASHBOARD_WIDGET_MAX_ROWS		(32)
#define SCREEN_MAX_ROWS				(100)
#define SCREEN_MAX_COLS				(100)

#undef SCREEN_RESOURCE_CLOCK
#define SCREEN_RESOURCE_CLOCK			(7)
#undef SCREEN_RESOURCE_GRAPH
#define SCREEN_RESOURCE_GRAPH			(0)
#undef SCREEN_RESOURCE_SIMPLE_GRAPH
#define SCREEN_RESOURCE_SIMPLE_GRAPH		(1)
#undef SCREEN_RESOURCE_LLD_GRAPH
#define SCREEN_RESOURCE_LLD_GRAPH		(20)
#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
#define SCREEN_RESOURCE_LLD_SIMPLE_GRAPH	(19)
#undef SCREEN_RESOURCE_PLAIN_TEXT
#define SCREEN_RESOURCE_PLAIN_TEXT		(3)
#undef SCREEN_RESOURCE_URL
#define SCREEN_RESOURCE_URL			(11)

#undef SCREEN_RESOURCE_MAP
#define SCREEN_RESOURCE_MAP			(2)
#undef SCREEN_RESOURCE_HOST_INFO
#define SCREEN_RESOURCE_HOST_INFO		(4)
#undef SCREEN_RESOURCE_TRIGGER_INFO
#define SCREEN_RESOURCE_TRIGGER_INFO		(5)
#undef SCREEN_RESOURCE_SERVER_INFO
#define SCREEN_RESOURCE_SERVER_INFO		(6)
#undef SCREEN_RESOURCE_TRIGGER_OVERVIEW
#define SCREEN_RESOURCE_TRIGGER_OVERVIEW	(9)
#undef SCREEN_RESOURCE_DATA_OVERVIEW
#define SCREEN_RESOURCE_DATA_OVERVIEW		(10)
#undef SCREEN_RESOURCE_ACTIONS
#define SCREEN_RESOURCE_ACTIONS			(12)
#undef SCREEN_RESOURCE_EVENTS
#define SCREEN_RESOURCE_EVENTS			(13)
#undef SCREEN_RESOURCE_HOSTGROUP_TRIGGERS
#define SCREEN_RESOURCE_HOSTGROUP_TRIGGERS	(14)
#undef SCREEN_RESOURCE_SYSTEM_STATUS
#define SCREEN_RESOURCE_SYSTEM_STATUS		(15)
#undef SCREEN_RESOURCE_HOST_TRIGGERS
#define SCREEN_RESOURCE_HOST_TRIGGERS		(16)

#define ZBX_WIDGET_FIELD_TYPE_INT32		(0)
#define ZBX_WIDGET_FIELD_TYPE_STR		(1)
#define ZBX_WIDGET_FIELD_TYPE_GROUP		(2)
#define ZBX_WIDGET_FIELD_TYPE_HOST		(3)
#define ZBX_WIDGET_FIELD_TYPE_ITEM		(4)
#define ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE	(5)
#define ZBX_WIDGET_FIELD_TYPE_GRAPH		(6)
#define ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE	(7)
#define ZBX_WIDGET_FIELD_TYPE_MAP		(8)

/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH				(0) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH			(1) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE		(2) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE	(3) */

#define ZBX_WIDGET_TYPE_CLOCK			("clock")
#define ZBX_WIDGET_TYPE_GRAPH_CLASSIC		("graph")
#define ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE		("graphprototype")
#define ZBX_WIDGET_TYPE_PLAIN_TEXT		("plaintext")
#define ZBX_WIDGET_TYPE_URL			("url")
#define ZBX_WIDGET_TYPE_ACTIONS			("actionlog")
#define ZBX_WIDGET_TYPE_DATA_OVERVIEW		("dataover")
#define ZBX_WIDGET_TYPE_PROBLEMS		("problems")
#define ZBX_WIDGET_TYPE_HOST_INFO		("hostavail")
#define ZBX_WIDGET_TYPE_MAP			("map")
#define ZBX_WIDGET_TYPE_SYSTEM_STATUS		("problemsbysv")
#define ZBX_WIDGET_TYPE_SERVER_INFO		("systeminfo")
#define ZBX_WIDGET_TYPE_TRIGGER_OVERVIEW	("trigover")

#define POS_EMPTY	(127)
#define POS_TAKEN	(1)

ZBX_VECTOR_DECL(scitem_dim2, zbx_screen_item_dim_t)
ZBX_VECTOR_IMPL(scitem_dim2, zbx_screen_item_dim_t)
ZBX_VECTOR_DECL(char2, char)
ZBX_VECTOR_IMPL(char2, char)

#define SKIP_EMPTY(vector,index)	if (POS_EMPTY == vector->values[index]) continue

#define COLLISIONS_MAX_NUMBER	(100)
#define REFERENCE_MAX_LEN	(5)
#define DASHBOARD_NAME_LEN	(255)

static int DBpatch_dashboard_name(char *name, char **new_name)
{
	int		affix = 0, ret = FAIL, trim;
	char		*affix_string = NULL;
	zbx_db_result_t	result = NULL;
	zbx_db_row_t	row;

	*new_name = zbx_strdup(*new_name, name);

	do
	{
		char	*name_esc;

		zbx_db_free_result(result);

		name_esc = zbx_db_dyn_escape_string(*new_name);

		result = zbx_db_select("select count(*)"
				" from dashboard"
				" where name='%s' and templateid is null",
				name_esc);

		zbx_free(name_esc);

		if (NULL == result || NULL == (row = zbx_db_fetch(result)))
		{
			zbx_free(*new_name);
			break;
		}

		if (0 == strcmp("0", row[0]))
		{
			ret = SUCCEED;
			break;
		}

		affix_string = zbx_dsprintf(affix_string, " (%d)", affix + 1);
		trim = (int)strlen(name) + (int)strlen(affix_string) - DASHBOARD_NAME_LEN;
		if (0 < trim )
			name[(int)strlen(name) - trim] = '\0';

		*new_name = zbx_dsprintf(*new_name, "%s%s", name, affix_string);
	} while (COLLISIONS_MAX_NUMBER > affix++);

	zbx_db_free_result(result);
	zbx_free(affix_string);

	return ret;
}

static int DBpatch_reference_name(char **ref_name)
{
	int		i = 0, j, ret = FAIL;
	char		name[REFERENCE_MAX_LEN + 1];
	const char	*pattern = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	zbx_db_result_t	result = NULL;
	zbx_db_row_t	row;

	name[REFERENCE_MAX_LEN] = '\0';

	do
	{
		for (j = 0; j < REFERENCE_MAX_LEN; j++)
			name[j] = pattern[rand() % (int)strlen(pattern)];

		zbx_db_free_result(result);

		result = zbx_db_select("select count(*)"
			" from widget_field"
			" where value_str='%s' and name='reference'",
			name);

		if (NULL == result || NULL == (row = zbx_db_fetch(result)))
			break;

		if (0 == strcmp("0", row[0]))
		{
			ret = SUCCEED;
			*ref_name = zbx_strdup(NULL, name);
			break;
		}

	} while (COLLISIONS_MAX_NUMBER > i++);

	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_init_dashboard(zbx_db_dashboard_t *dashboard, char *name, uint64_t userid,
		int private)
{
	int	ret = SUCCEED;

	memset((void *)dashboard, 0, sizeof(zbx_db_dashboard_t));

	dashboard->userid = userid;
	dashboard->private = private;
	dashboard->display_period = 30;
	ret = DBpatch_dashboard_name(name, &dashboard->name);

	return ret;
}

static void	DBpatch_widget_field_free(zbx_db_widget_field_t *field)
{
	zbx_free(field->name);
	zbx_free(field->value_str);
	zbx_free(field);
}

static void	DBpatch_screen_item_free(zbx_db_screen_item_t *si)
{
	zbx_free(si->url);
	zbx_free(si->application);
	zbx_free(si);
}

static size_t	DBpatch_array_max_used_index(char *array, size_t arr_size)
{
	size_t	i, m = 0;

	for (i = 0; i < arr_size; i++)
	{
		if (0 != array[i])
			m = i;
	}

	return m;
}

static void DBpatch_normalize_screen_items_pos(zbx_vector_ptr_t *scr_items)
{
	char	used_x[SCREEN_MAX_COLS], used_y[SCREEN_MAX_ROWS];
	char	keep_x[SCREEN_MAX_COLS], keep_y[SCREEN_MAX_ROWS];
	int	i, n, x;

	memset((void *)used_x, 0, sizeof(used_x));
	memset((void *)used_y, 0, sizeof(used_y));
	memset((void *)keep_x, 0, sizeof(keep_x));
	memset((void *)keep_y, 0, sizeof(keep_y));

	for (i = 0; i < scr_items->values_num; i++)
	{
		zbx_db_screen_item_t	*c = (zbx_db_screen_item_t *)scr_items->values[i];

		for (n = c->x; n < c->x + c->colspan && n < SCREEN_MAX_COLS; n++)
			used_x[n] = 1;
		for (n = c->y; n < c->y + c->rowspan && n < SCREEN_MAX_ROWS; n++)
			used_y[n] = 1;

		keep_x[c->x] = 1;
		if (c->x + c->colspan < SCREEN_MAX_COLS)
			keep_x[c->x + c->colspan] = 1;
		keep_y[c->y] = 1;
		if (c->y + c->rowspan < SCREEN_MAX_ROWS)
			keep_y[c->y + c->rowspan] = 1;
	}

#define COMPRESS_SCREEN_ITEMS(axis, span, a_size)							\
													\
do {													\
	for (x = (int)DBpatch_array_max_used_index(keep_ ## axis, a_size); x >= 0; x--)			\
	{												\
		if (0 != keep_ ## axis[x] && 0 != used_ ## axis[x])					\
			continue;									\
													\
		for (i = 0; i < scr_items->values_num; i++)						\
		{											\
			zbx_db_screen_item_t	*c = (zbx_db_screen_item_t *)scr_items->values[i];	\
													\
			if (x < c->axis)								\
				c->axis--;								\
													\
			if (x > c->axis && x < c->axis + c->span)					\
				c->span--;								\
		}											\
	}												\
} while (0)

	COMPRESS_SCREEN_ITEMS(x, colspan, SCREEN_MAX_COLS);
	COMPRESS_SCREEN_ITEMS(y, rowspan, SCREEN_MAX_ROWS);

#undef COMPRESS_SCREEN_ITEMS
}

static void	DBpatch_get_preferred_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
{
	*w = item->width;
	*h = item->height;

	if (SCREEN_RESOURCE_LLD_GRAPH == item->resourcetype || SCREEN_RESOURCE_LLD_SIMPLE_GRAPH == item->resourcetype ||
			SCREEN_RESOURCE_GRAPH == item->resourcetype ||
			SCREEN_RESOURCE_SIMPLE_GRAPH == item->resourcetype)
	{
		*h += 215;	/* SCREEN_LEGEND_HEIGHT */
	}

	if (SCREEN_RESOURCE_PLAIN_TEXT == item->resourcetype || SCREEN_RESOURCE_HOST_INFO == item->resourcetype ||
			SCREEN_RESOURCE_TRIGGER_INFO == item->resourcetype ||
			SCREEN_RESOURCE_SERVER_INFO == item->resourcetype ||
			SCREEN_RESOURCE_ACTIONS == item->resourcetype ||
			SCREEN_RESOURCE_EVENTS == item->resourcetype ||
			SCREEN_RESOURCE_HOSTGROUP_TRIGGERS == item->resourcetype ||
			SCREEN_RESOURCE_SYSTEM_STATUS == item->resourcetype ||
			SCREEN_RESOURCE_HOST_TRIGGERS== item->resourcetype)
	{
		*h = 2 + 2 * MIN(25, item->elements) / 5;
	}
	else
		*h = (int)round((double)*h / 70);				/* WIDGET_ROW_HEIGHT */

	*w = (int)round((double)*w / 1920 * DASHBOARD_MAX_COLS);	/* DISPLAY_WIDTH */

	*w = MIN(DASHBOARD_MAX_COLS, MAX(1, *w));
	*h = MIN(DASHBOARD_WIDGET_MAX_ROWS, MAX(DASHBOARD_WIDGET_MIN_ROWS, *h));
}

static void	DBpatch_get_min_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
{
	switch (item->resourcetype)
	{
		case SCREEN_RESOURCE_CLOCK:
			*w = 1; *h = 2;
			break;
		case SCREEN_RESOURCE_GRAPH:
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
		case SCREEN_RESOURCE_LLD_GRAPH:
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
		case SCREEN_RESOURCE_MAP:
			*w = 4; *h = 4;
			break;
		case SCREEN_RESOURCE_PLAIN_TEXT:
		case SCREEN_RESOURCE_URL:
		case SCREEN_RESOURCE_TRIGGER_INFO:
		case SCREEN_RESOURCE_ACTIONS:
		case SCREEN_RESOURCE_EVENTS:
		case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
		case SCREEN_RESOURCE_HOST_TRIGGERS:
			*w = 4; *h = 2;
			break;
		case SCREEN_RESOURCE_HOST_INFO:
			*w = 4; *h = 3;
			break;
		case SCREEN_RESOURCE_SERVER_INFO:
		case SCREEN_RESOURCE_SYSTEM_STATUS:
			*w = 4; *h = 4;
			break;
		case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
			*w = 4; *h = 7;
			break;
		case SCREEN_RESOURCE_DATA_OVERVIEW:
			*w = 4; *h = 5;
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown resource type %d", __func__, item->resourcetype);
	}
}

static char	*lw_array_to_str(zbx_vector_char2_t *v)
{
	static char	str[MAX_STRING_LEN];
	char		*ptr;
	int		i, max = MAX_STRING_LEN, len;

	ptr = str;
	len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
	ptr += len;
	max -= len;

	for (i = 0; 0 < max && i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
		{
			len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, (int)v->values[i]);
			ptr += len;
			max -= len;
		}
	}

	if (max > 1)
		strcat(ptr, "]");

	return str;
}

static void	lw_array_debug(char *pfx, zbx_vector_char2_t *v)
{
	zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, lw_array_to_str(v));
}

static void	int_array_debug(char *pfx, int *a, int alen, int emptyval)
{
	static char	str[MAX_STRING_LEN];
	char		*ptr;
	int		i, max = MAX_STRING_LEN, len;

	ptr = str;
	len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
	ptr += len;
	max -= len;

	for (i = 0; 0 < max && i < alen; i++)
	{
		if (emptyval != a[i])
		{
			len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, a[i]);
			ptr += len;
			max -= len;
		}
	}

	if (max > 1)
		strcat(ptr, "]");

	zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, str);
}

static zbx_vector_char2_t	*lw_array_create(void)
{
	zbx_vector_char2_t	*v;
	static char		fill[SCREEN_MAX_ROWS];

	if (0 == fill[0])
		memset(fill, POS_EMPTY, SCREEN_MAX_ROWS);

	v = (zbx_vector_char2_t *)malloc(sizeof(zbx_vector_char2_t));

	zbx_vector_char2_create(v);
	zbx_vector_char2_append_array(v, fill, SCREEN_MAX_ROWS);

	return v;
}

static void	lw_array_free(zbx_vector_char2_t *v)
{
	if (NULL != v)
	{
		zbx_vector_char2_destroy(v);
		zbx_free(v);
	}
}

static zbx_vector_char2_t	*lw_array_create_fill(int start, size_t num)
{
	size_t			i;
	zbx_vector_char2_t	*v;

	v = lw_array_create();

	for (i = (size_t)start; i < (size_t)start + num && i < (size_t)v->values_num; i++)
		v->values[i] = POS_TAKEN;

	return v;
}

static zbx_vector_char2_t	*lw_array_diff(zbx_vector_char2_t *a, zbx_vector_char2_t *b)
{
	int			i;
	zbx_vector_char2_t	*v;

	v = lw_array_create();

	for (i = 0; i < a->values_num; i++)
	{
		SKIP_EMPTY(a, i);
		if (POS_EMPTY == b->values[i])
			v->values[i] = a->values[i];
	}

	return v;
}

static zbx_vector_char2_t	*lw_array_intersect(zbx_vector_char2_t *a, zbx_vector_char2_t *b)
{
	int			i;
	zbx_vector_char2_t	*v;

	v = lw_array_create();

	for (i = 0; i < a->values_num; i++)
	{
		SKIP_EMPTY(a, i);
		if (POS_EMPTY != b->values[i])
			v->values[i] = a->values[i];
	}

	return v;
}

static int	lw_array_count(zbx_vector_char2_t *v)
{
	int	i, c = 0;

	for (i = 0; i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
			c++;
	}

	return c;
}

static int	lw_array_sum(zbx_vector_char2_t *v)
{
	int	i, c = 0;

	for (i = 0; i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
			c += v->values[i];
	}

	return c;
}

typedef struct
{
	int			index;	/* index for zbx_vector_scitem_dim2_t */
	zbx_vector_char2_t	*r_block;
}
sciitem_block_t;

static zbx_vector_char2_t	*sort_dimensions;

static int	DBpatch_block_compare_func(const void *d1, const void *d2)
{
	const sciitem_block_t	*i1 = *(const sciitem_block_t * const *)d1;
	const sciitem_block_t	*i2 = *(const sciitem_block_t * const *)d2;
	zbx_vector_char2_t	*diff1, *diff2;
	int			unsized_a, unsized_b;

	diff1 = lw_array_diff(i1->r_block, sort_dimensions);
	diff2 = lw_array_diff(i2->r_block, sort_dimensions);

	unsized_a = lw_array_count(diff1);
	unsized_b = lw_array_count(diff2);

	lw_array_free(diff1);
	lw_array_free(diff2);

	ZBX_RETURN_IF_NOT_EQUAL(unsized_a, unsized_b);

	return 0;
}

static zbx_vector_char2_t	*DBpatch_get_axis_dimensions(zbx_vector_scitem_dim2_t *scitems)
{
	int			i;
	zbx_vector_ptr_t	blocks;
	sciitem_block_t		*block;
	zbx_vector_char2_t	*dimensions;

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

	zbx_vector_ptr_create(&blocks);
	dimensions = lw_array_create();

	for (i = 0; i < scitems->values_num; i++)
	{
		block = (sciitem_block_t *)malloc(sizeof(sciitem_block_t));
		block->r_block = lw_array_create_fill(scitems->values[i].position, (size_t)scitems->values[i].span);
		block->index = i;
		zbx_vector_ptr_append(&blocks, (void *)block);
	}

	sort_dimensions = dimensions;

	while (0 < blocks.values_num)
	{
		zbx_vector_char2_t	*block_dimensions, *block_unsized, *r_block;
		int			block_dimensions_sum, block_unsized_count, size_overflow, n;

		zbx_vector_ptr_sort(&blocks, DBpatch_block_compare_func);
		block = blocks.values[0];
		r_block = block->r_block;

		block_dimensions = lw_array_intersect(dimensions, r_block);
		block_dimensions_sum = lw_array_sum(block_dimensions);
		lw_array_free(block_dimensions);

		block_unsized = lw_array_diff(r_block, dimensions);
		block_unsized_count = lw_array_count(block_unsized);
		size_overflow = scitems->values[block->index].size - block_dimensions_sum;

		if (0 < block_unsized_count)
		{
			for (n = 0; n < block_unsized->values_num; n++)
			{
				SKIP_EMPTY(block_unsized, n);
				dimensions->values[n] = (char)MAX(1, size_overflow / block_unsized_count);
				size_overflow -= dimensions->values[n];
				block_unsized_count--;
			}
		}
		else if (0 < size_overflow)
		{
			for (n = 0; n < r_block->values_num; n++)
			{
				double	factor;
				int	new_dimension;

				SKIP_EMPTY(r_block, n);
				factor = (double)(size_overflow + block_dimensions_sum) / block_dimensions_sum;
				new_dimension = (int)round(factor * dimensions->values[n]);
				block_dimensions_sum -= dimensions->values[n];
				size_overflow -= new_dimension - dimensions->values[n];
				dimensions->values[n] = (char)new_dimension;
			}
		}

		lw_array_free(block->r_block);
		zbx_free(block);
		lw_array_free(block_unsized);
		zbx_vector_ptr_remove(&blocks, 0);
	}

	zbx_vector_ptr_destroy(&blocks);

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): dim:%s", __func__, lw_array_to_str(dimensions));

	return dimensions;
}

/* modifies widget units in first argument */
static void	DBpatch_adjust_axis_dimensions(zbx_vector_char2_t *d, zbx_vector_char2_t *d_min, int target)
{
	int	dimensions_sum, i;

	zabbix_log(LOG_LEVEL_TRACE, "In %s(): d:%s", __func__, lw_array_to_str(d));
	zabbix_log(LOG_LEVEL_TRACE, "  d_min:%s", lw_array_to_str(d_min));

	dimensions_sum = lw_array_sum(d);

	while (dimensions_sum != target)
	{
		int	potential_index = -1;
		double	potential_value;

		for (i = 0; i < d->values_num; i++)
		{
			double	value;

			SKIP_EMPTY(d, i);
			value = (double)d->values[i] / d_min->values[i];

			if (0 > potential_index ||
					(dimensions_sum > target && value > potential_value) ||
					(dimensions_sum < target && value < potential_value))
			{
				potential_index = i;
				potential_value = value;
			}
		}

		if (0 <= potential_index)
		{
			zabbix_log(LOG_LEVEL_TRACE, "dim_sum:%d pot_idx/val:%d/%.2lf", dimensions_sum,
					potential_index, potential_value);
		}

		if (dimensions_sum > target && d->values[potential_index] == d_min->values[potential_index])
			break;

		if (dimensions_sum > target)
		{
			d->values[potential_index]--;
			dimensions_sum--;
		}
		else
		{
			d->values[potential_index]++;
			dimensions_sum++;
		}
	}

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): d:%s", __func__, lw_array_to_str(d));
}

static void	DBpatch_get_dashboard_dimensions(zbx_vector_ptr_t *scr_items, zbx_vector_char2_t **x,
		zbx_vector_char2_t **y)
{
	zbx_vector_char2_t		*dim_x_pref, *dim_x_min;
	zbx_vector_char2_t		*dim_y_pref, *dim_y_min;
	zbx_vector_scitem_dim2_t	items_x_pref, items_y_pref;
	zbx_vector_scitem_dim2_t	items_x_min, items_y_min;
	int				i;

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

	zbx_vector_scitem_dim2_create(&items_x_pref);
	zbx_vector_scitem_dim2_create(&items_y_pref);
	zbx_vector_scitem_dim2_create(&items_x_min);
	zbx_vector_scitem_dim2_create(&items_y_min);

	for (i = 0; i < scr_items->values_num; i++)
	{
		int			pref_size_w, pref_size_h;
		int			min_size_w, min_size_h;
		zbx_screen_item_dim_t	item;
		zbx_db_screen_item_t	*si;

		si = scr_items->values[i];
		DBpatch_get_preferred_widget_size(si, &pref_size_w, &pref_size_h);
		DBpatch_get_min_widget_size(si, &min_size_w, &min_size_h);

		item.position = si->x;
		item.span = si->colspan;
		item.size = MAX(pref_size_w, min_size_w);
		zbx_vector_scitem_dim2_append(&items_x_pref, item);

		item.position = si->y;
		item.span = si->rowspan;
		item.size = MAX(pref_size_h, min_size_h);
		zbx_vector_scitem_dim2_append(&items_y_pref, item);

		item.position = si->x;
		item.span = si->colspan;
		item.size = min_size_w;
		zbx_vector_scitem_dim2_append(&items_x_min, item);

		item.position = si->y;
		item.span = si->rowspan;
		item.size = min_size_h;
		zbx_vector_scitem_dim2_append(&items_y_min, item);
	}

	dim_x_pref = DBpatch_get_axis_dimensions(&items_x_pref);
	dim_x_min = DBpatch_get_axis_dimensions(&items_x_min);

	zabbix_log(LOG_LEVEL_TRACE, "%s: dim_x_pref:%s", __func__, lw_array_to_str(dim_x_pref));
	zabbix_log(LOG_LEVEL_TRACE, "  dim_x_min:%s", lw_array_to_str(dim_x_min));

	DBpatch_adjust_axis_dimensions(dim_x_pref, dim_x_min, DASHBOARD_MAX_COLS);

	dim_y_pref = DBpatch_get_axis_dimensions(&items_y_pref);
	dim_y_min = DBpatch_get_axis_dimensions(&items_y_min);

	if (DASHBOARD_MAX_ROWS < lw_array_sum(dim_y_pref))
		DBpatch_adjust_axis_dimensions(dim_y_pref, dim_y_min, DASHBOARD_MAX_ROWS);

	lw_array_free(dim_x_min);
	lw_array_free(dim_y_min);
	zbx_vector_scitem_dim2_destroy(&items_x_pref);
	zbx_vector_scitem_dim2_destroy(&items_y_pref);
	zbx_vector_scitem_dim2_destroy(&items_x_min);
	zbx_vector_scitem_dim2_destroy(&items_y_min);

	*x = dim_x_pref;
	*y = dim_y_pref;

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): x:%s y:%s", __func__, lw_array_to_str(*x), lw_array_to_str(*y));
}

static zbx_db_widget_field_t	*DBpatch_make_widget_field(int type, char *name, void *value)
{
	zbx_db_widget_field_t	*wf;

	wf = (zbx_db_widget_field_t *)zbx_calloc(NULL, 1, sizeof(zbx_db_widget_field_t));
	wf->name = zbx_strdup(NULL, name);
	wf->type = type;

	switch (type)
	{
		case ZBX_WIDGET_FIELD_TYPE_INT32:
			wf->value_int = *((int *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_STR:
			wf->value_str = zbx_strdup(NULL, (char *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_GROUP:
			wf->value_groupid = *((uint64_t *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_HOST:
			wf->value_hostid = *((uint64_t *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_ITEM:
		case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE:
			wf->value_itemid = *((uint64_t *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_GRAPH:
		case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE:
			wf->value_graphid = *((uint64_t *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_MAP:
			wf->value_sysmapid = *((uint64_t *)value);
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown field type: %d", __func__, type);
	}

	if (NULL == wf->value_str)
		wf->value_str = zbx_strdup(NULL, "");

	return wf;
}

static void DBpatch_widget_from_screen_item(zbx_db_screen_item_t *si, zbx_db_widget_t *w, zbx_vector_ptr_t *fields)
{
	int			tmp;
	char			*reference = NULL;
	zbx_db_widget_field_t	*f;

	w->name = zbx_strdup(NULL, "");
	w->view_mode = 0;	/* ZBX_WIDGET_VIEW_MODE_NORMAL */

#define ADD_FIELD(a, b, c)				\
							\
do {							\
	f = DBpatch_make_widget_field(a, b, c);		\
	zbx_vector_ptr_append(fields, (void *)f);	\
} while (0)

	switch (si->resourcetype)
	{
		case SCREEN_RESOURCE_CLOCK:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_CLOCK);

			/* here are below in this switch we add only those fields that are not */
			/* considered default by frontend API */

			if (0 != si->style)	/* style 0 is default, don't add */
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "time_type", (void *)&si->style);
			if (2 == si->style)	/* TIME_TYPE_HOST */
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
			break;
		case SCREEN_RESOURCE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
			/* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0); don't add because it's default */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH, "graphid", (void *)&si->resourceid);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
			tmp = 1;	/* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_LLD_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
			/* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2); don't add because it's default */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE, "graphid", (void *)&si->resourceid);
			/* add field "columns" because the default value is 2 */
			tmp = 1;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			/* don't add field "rows" because 1 is default */
			break;
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
			tmp = 3;	/* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE, "itemid", (void *)&si->resourceid);
			tmp = 1;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			/* don't add field "rows" because 1 is default */
			break;
		case SCREEN_RESOURCE_PLAIN_TEXT:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PLAIN_TEXT);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemids", (void *)&si->resourceid);
			if (0 != si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_as_html", (void *)&si->style);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_URL:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_URL);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "url", (void *)si->url);
			tmp = 1;
			if (1 == si->dynamic)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "dynamic", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_ACTIONS:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_ACTIONS);
			if (4 != si->sort_triggers)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			break;
		case SCREEN_RESOURCE_DATA_OVERVIEW:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_DATA_OVERVIEW);
			tmp = 1;
			if (1 == si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "style", (void *)&tmp);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
			if ('\0' != *si->application)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "application", (void *)si->application);
			break;
		case SCREEN_RESOURCE_EVENTS:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			tmp = 2;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
			if (0 != si->sort_triggers)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			if (0 != si->resourceid)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
			tmp = 3;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
			tmp = 0;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_timeline", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_HOST_INFO:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_HOST_INFO);
			tmp = 1;
			if (1 == si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "layout", (void *)&tmp);
			if (0 != si->resourceid)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
			break;
		case SCREEN_RESOURCE_HOST_TRIGGERS:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PROBLEMS);
			if (0 != si->sort_triggers)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "sort_triggers", (void *)&si->sort_triggers);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			if (0 != si->resourceid)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_HOST, "hostids", (void *)&si->resourceid);
			tmp = 3;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
			tmp = 0;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_timeline", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_MAP:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_MAP);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_MAP, "sysmapid", (void *)&si->resourceid);
			if (SUCCEED == DBpatch_reference_name(&reference))
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "reference", (void *)reference);
			zbx_free(reference);
			break;
		case SCREEN_RESOURCE_SYSTEM_STATUS:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SYSTEM_STATUS);
			break;
		case SCREEN_RESOURCE_SERVER_INFO:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SERVER_INFO);
			break;
		case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_TRIGGER_OVERVIEW);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
			if ('\0' != *si->application)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "application", (void *)si->application);
			tmp = 1;
			if (1 == si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "style", (void *)&tmp);
			tmp = 2;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show", (void *)&tmp);
			break;
		case SCREEN_RESOURCE_TRIGGER_INFO:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_SYSTEM_STATUS);
			if (0 != si->resourceid)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GROUP, "groupids", (void *)&si->resourceid);
			tmp = 1;
			if (1 == si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "layout", (void *)&tmp);
			tmp = 1;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_type", (void *)&tmp);
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown screen resource type: %d", __func__,
					si->resourcetype);
	}
#undef ADD_FIELD
}

static char	*DBpatch_resourcetype_str(int rtype)
{
	switch (rtype)
	{
		case SCREEN_RESOURCE_CLOCK:
			return "clock";
		case SCREEN_RESOURCE_GRAPH:
			return "graph";
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
			return "simplegraph";
		case SCREEN_RESOURCE_LLD_GRAPH:
			return "lldgraph";
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
			return "lldsimplegraph";
		case SCREEN_RESOURCE_PLAIN_TEXT:
			return "plaintext";
		case SCREEN_RESOURCE_URL:
			return "url";
		/* additional types */
		case SCREEN_RESOURCE_MAP:
			return "map";
		case SCREEN_RESOURCE_HOST_INFO:
			return "host info";
		case SCREEN_RESOURCE_TRIGGER_INFO:
			return "trigger info";
		case SCREEN_RESOURCE_SERVER_INFO:
			return "server info";
		case SCREEN_RESOURCE_TRIGGER_OVERVIEW:
			return "trigger overview";
		case SCREEN_RESOURCE_DATA_OVERVIEW:
			return "data overview";
		case SCREEN_RESOURCE_ACTIONS:
			return "action";
		case SCREEN_RESOURCE_EVENTS:
			return "events";
		case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS:
			return "hostgroup triggers";
		case SCREEN_RESOURCE_SYSTEM_STATUS:
			return "system status";
		case SCREEN_RESOURCE_HOST_TRIGGERS:
			return "host triggers";
	}

	return "*unknown*";
}

static void	DBpatch_trace_screen_item(zbx_db_screen_item_t *item)
{
	zabbix_log(LOG_LEVEL_TRACE, "    screenitemid:" ZBX_FS_UI64 " screenid:" ZBX_FS_UI64,
			item->screenitemid, item->screenid);
	zabbix_log(LOG_LEVEL_TRACE, "        resourcetype: %s resourceid:" ZBX_FS_UI64,
			DBpatch_resourcetype_str(item->resourcetype), item->resourceid);
	zabbix_log(LOG_LEVEL_TRACE, "        w/h: %dx%d (x,y): (%d,%d) (c,rspan): (%d,%d)",
			item->width, item->height, item->x, item->y, item->colspan, item->rowspan);
}

static void	DBpatch_trace_widget(zbx_db_widget_t *w)
{
	zabbix_log(LOG_LEVEL_TRACE, "    widgetid:" ZBX_FS_UI64 " dbid:" ZBX_FS_UI64 " type:%s",
			w->widgetid, w->dashboardid, w->type);
	zabbix_log(LOG_LEVEL_TRACE, "    widget type: %s w/h: %dx%d (x,y): (%d,%d)",
			w->type, w->width, w->height, w->x, w->y);
}

/* adds new dashboard to the DB, sets new dashboardid in the struct */
static int 	DBpatch_add_dashboard(zbx_db_dashboard_t *dashboard)
{
	char	*name_esc;
	int	res;

	dashboard->dashboardid = zbx_db_get_maxid("dashboard");
	name_esc = zbx_db_dyn_escape_string(dashboard->name);

	zabbix_log(LOG_LEVEL_TRACE, "adding dashboard id:" ZBX_FS_UI64, dashboard->dashboardid);

	res = zbx_db_execute("insert into dashboard (dashboardid,name,userid,private,display_period) values "
			"("ZBX_FS_UI64 ",'%s',"ZBX_FS_UI64 ",%d,%d)",
			dashboard->dashboardid, name_esc, dashboard->userid, dashboard->private,
			dashboard->display_period);

	zbx_free(name_esc);

	return ZBX_DB_OK > res ? FAIL : SUCCEED;
}

/* adds new dashboard page to the DB, sets new dashboard_pageid in the struct */
static int 	DBpatch_add_dashboard_page(zbx_db_dashboard_page_t *dashboard_page, uint64_t dashboardid, char *name,
		int display_period, int sortorder)
{
	int	res;
	char	*name_esc;

	dashboard_page->dashboard_pageid = zbx_db_get_maxid("dashboard_page");
	dashboard_page->dashboardid = dashboardid;

	zabbix_log(LOG_LEVEL_TRACE, "adding dashboard_page id:" ZBX_FS_UI64, dashboard_page->dashboard_pageid);

	name_esc = zbx_db_dyn_escape_string(name);
	res = zbx_db_execute("insert into dashboard_page (dashboard_pageid,dashboardid,name,display_period,sortorder)"
			" values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ",'%s',%d,%d)",
			dashboard_page->dashboard_pageid, dashboardid, name_esc, display_period, sortorder);
	zbx_free(name_esc);

	return ZBX_DB_OK > res ? FAIL : SUCCEED;
}

/* adds new widget and widget fields to the DB */
static int	DBpatch_add_widget(uint64_t dashboardid, zbx_db_widget_t *widget, zbx_vector_ptr_t *fields)
{
	uint64_t	new_fieldid;
	int		i, ret = SUCCEED;
	char		*name_esc;

	widget->widgetid = zbx_db_get_maxid("widget");
	widget->dashboardid = dashboardid;
	name_esc = zbx_db_dyn_escape_string(widget->name);

	zabbix_log(LOG_LEVEL_TRACE, "adding widget id: " ZBX_FS_UI64 ", type: %s", widget->widgetid, widget->type);

	if (ZBX_DB_OK > zbx_db_execute("insert into widget (widgetid,dashboard_pageid,type,name,x,y,width,height,view_mode) "
			"values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s','%s',%d,%d,%d,%d,%d)",
			widget->widgetid, widget->dashboardid, widget->type, name_esc,
			widget->x, widget->y, widget->width, widget->height, widget->view_mode))
	{
		ret = FAIL;
	}

	zbx_free(name_esc);

	if (SUCCEED == ret && 0 < fields->values_num)
		new_fieldid = zbx_db_get_maxid_num("widget_field", fields->values_num);

	for (i = 0; SUCCEED == ret && i < fields->values_num; i++)
	{
		char			*url_esc;
		zbx_db_widget_field_t	*f;

		f = (zbx_db_widget_field_t *)fields->values[i];
		url_esc = zbx_db_dyn_escape_string(f->value_str);

		if (ZBX_DB_OK > zbx_db_execute("insert into widget_field (widget_fieldid,widgetid,type,name,value_int,"
				"value_str,value_itemid,value_graphid,value_groupid,value_hostid,value_sysmapid)"
				" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,'%s',%d,'%s',%s,%s,%s,%s,%s)",
				new_fieldid++, widget->widgetid, f->type, f->name, f->value_int, url_esc,
				zbx_db_sql_id_ins(f->value_itemid), zbx_db_sql_id_ins(f->value_graphid),
				zbx_db_sql_id_ins(f->value_groupid), zbx_db_sql_id_ins(f->value_hostid),
				zbx_db_sql_id_ins(f->value_sysmapid)))
		{
			ret = FAIL;
		}

		zbx_free(url_esc);
	}

	return ret;
}

static int DBpatch_set_permissions_screen(uint64_t dashboardid, uint64_t screenid)
{
	int		ret = SUCCEED;
	zbx_db_result_t	result;
	zbx_db_row_t	row;

	result = zbx_db_select("select userid,permission from screen_user where screenid=" ZBX_FS_UI64, screenid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into dashboard_user (dashboard_userid,dashboardid,userid,permission)"
			" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%s,%s)",
			zbx_db_get_maxid("dashboard_user"), dashboardid, row[0], row[1]))
		{
			ret = FAIL;
			goto out;
		}
	}
	zbx_db_free_result(result);

	result = zbx_db_select("select usrgrpid,permission from screen_usrgrp where screenid=" ZBX_FS_UI64, screenid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into dashboard_usrgrp"
			" (dashboard_usrgrpid,dashboardid,usrgrpid,permission)"
			" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%s,%s)",
			zbx_db_get_maxid("dashboard_usrgrp"), dashboardid, row[0], row[1]))
		{
			ret = FAIL;
			goto out;
		}
	}
out:
	zbx_db_free_result(result);

	return ret;
}

static int DBpatch_set_permissions_slideshow(uint64_t dashboardid, uint64_t slideshowid)
{
	int		ret = SUCCEED;
	zbx_db_result_t	result;
	zbx_db_row_t	row;

	result = zbx_db_select("select userid,permission from slideshow_user where slideshowid=" ZBX_FS_UI64, slideshowid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into dashboard_user (dashboard_userid,dashboardid,userid,permission)"
			" values ("ZBX_FS_UI64 ","ZBX_FS_UI64 ",%s,%s)",
			zbx_db_get_maxid("dashboard_user"), dashboardid, row[0], row[1]))
		{
			ret = FAIL;
			goto out;
		}
	}
	zbx_db_free_result(result);

	result = zbx_db_select("select usrgrpid,permission from slideshow_usrgrp where slideshowid=" ZBX_FS_UI64,
			slideshowid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into dashboard_usrgrp"
			" (dashboard_usrgrpid,dashboardid,usrgrpid,permission)"
			" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%s,%s)",
			zbx_db_get_maxid("dashboard_usrgrp"), dashboardid, row[0], row[1]))
		{
			ret = FAIL;
			goto out;
		}
	}
out:
	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_delete_screen(uint64_t screenid)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from screens_items where screenid=" ZBX_FS_UI64, screenid))
		return FAIL;

	if (ZBX_DB_OK > zbx_db_execute("delete from screens where screenid=" ZBX_FS_UI64, screenid))
		return FAIL;

	return SUCCEED;
}

#define OFFSET_ARRAY_SIZE	(SCREEN_MAX_ROWS + 1)

static int	DBpatch_convert_screen_items(zbx_db_result_t result, uint64_t id)
{
	zbx_db_row_t		row;
	int			i, ret = SUCCEED;
	zbx_db_screen_item_t	*scr_item;
	zbx_vector_ptr_t	screen_items;
	zbx_vector_char2_t	*dim_x, *dim_y;
	int			offsets_x[OFFSET_ARRAY_SIZE], offsets_y[OFFSET_ARRAY_SIZE];

	zbx_vector_ptr_create(&screen_items);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		scr_item = (zbx_db_screen_item_t*)zbx_calloc(NULL, 1, sizeof(zbx_db_screen_item_t));

		ZBX_DBROW2UINT64(scr_item->screenitemid, row[0]);
		ZBX_DBROW2UINT64(scr_item->screenid, row[1]);
		scr_item->resourcetype = atoi(row[2]);
		ZBX_DBROW2UINT64(scr_item->resourceid, row[3]);
		scr_item->width = atoi(row[4]);
		scr_item->height = atoi(row[5]);
		scr_item->x = atoi(row[6]);
		scr_item->y = atoi(row[7]);
		scr_item->colspan = atoi(row[8]);
		scr_item->rowspan = atoi(row[9]);
		scr_item->elements = atoi(row[10]);
		scr_item->style = atoi(row[11]);
		scr_item->url = zbx_strdup(NULL, row[12]);
		scr_item->sort_triggers = atoi(row[13]);
		scr_item->application = zbx_strdup(NULL, row[14]);
		scr_item->dynamic = atoi(row[15]);

		if (0 == scr_item->colspan)
		{
			scr_item->colspan = 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: colspan is 0, converted to 1 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (0 == scr_item->rowspan)
		{
			scr_item->rowspan = 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: rowspan is 0, converted to 1 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (SCREEN_MAX_COLS <= scr_item->x)
		{
			scr_item->x = SCREEN_MAX_COLS - 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: x is more than %d, limited for item " ZBX_FS_UI64,
					scr_item->x, scr_item->screenitemid);
		}

		if (0 > scr_item->x)
		{
			scr_item->x = 0;
			zabbix_log(LOG_LEVEL_WARNING, "warning: x is negative, set to 0 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (SCREEN_MAX_ROWS <= scr_item->y)
		{
			scr_item->y = SCREEN_MAX_ROWS - 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: y is more than %d, limited for item " ZBX_FS_UI64,
					scr_item->y, scr_item->screenitemid);
		}

		if (0 > scr_item->y)
		{
			scr_item->y = 0;
			zabbix_log(LOG_LEVEL_WARNING, "warning: y is negative, set to 0 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		DBpatch_trace_screen_item(scr_item);

		zbx_vector_ptr_append(&screen_items, (void *)scr_item);
	}

	if (screen_items.values_num > 0)
	{
		zabbix_log(LOG_LEVEL_TRACE, "total %d screen items", screen_items.values_num);

		DBpatch_normalize_screen_items_pos(&screen_items);
		DBpatch_get_dashboard_dimensions(&screen_items, &dim_x, &dim_y);

		lw_array_debug("dim_x", dim_x);
		lw_array_debug("dim_y", dim_y);

		offsets_x[0] = 0;
		offsets_y[0] = 0;
		for (i = 1; i < OFFSET_ARRAY_SIZE; i++)
		{
			offsets_x[i] = -1;
			offsets_y[i] = -1;
		}

		for (i = 0; i < dim_x->values_num; i++)
		{
			if (POS_EMPTY != dim_x->values[i])
				offsets_x[i + 1] = i == 0 ? dim_x->values[i] : offsets_x[i] + dim_x->values[i];
			if (POS_EMPTY != dim_y->values[i])
				offsets_y[i + 1] = i == 0 ? dim_y->values[i] : offsets_y[i] + dim_y->values[i];
		}

		int_array_debug("offsets_x", offsets_x, OFFSET_ARRAY_SIZE, -1);
		int_array_debug("offsets_y", offsets_y, OFFSET_ARRAY_SIZE, -1);
	}

	for (i = 0; SUCCEED == ret && i < screen_items.values_num; i++)
	{
		int			offset_idx_x, offset_idx_y;
		zbx_db_widget_t		w;
		zbx_vector_ptr_t	widget_fields;
		zbx_db_screen_item_t	*si;

		si = screen_items.values[i];

		offset_idx_x = si->x + si->colspan;
		if (offset_idx_x > OFFSET_ARRAY_SIZE - 1)
		{
			offset_idx_x = OFFSET_ARRAY_SIZE - 1;
			zabbix_log(LOG_LEVEL_WARNING, "config error, x screen size overflow for item " ZBX_FS_UI64,
					si->screenitemid);
		}

		offset_idx_y = si->y + si->rowspan;
		if (offset_idx_y > OFFSET_ARRAY_SIZE - 1)
		{
			offset_idx_y = OFFSET_ARRAY_SIZE - 1;
			zabbix_log(LOG_LEVEL_WARNING, "config error, y screen size overflow for item " ZBX_FS_UI64,
					si->screenitemid);
		}

		memset((void *)&w, 0, sizeof(zbx_db_widget_t));
		w.x = offsets_x[si->x];
		w.y = offsets_y[si->y];
		w.width = offsets_x[offset_idx_x] - offsets_x[si->x];
		w.height = offsets_y[offset_idx_y] - offsets_y[si->y];

		/* skip screen items not fitting on the dashboard */
		if (w.x + w.width > DASHBOARD_MAX_COLS || w.y + w.height > DASHBOARD_MAX_ROWS)
		{
			zabbix_log(LOG_LEVEL_WARNING, "skipping screenitemid " ZBX_FS_UI64
					" (too wide, tall or offscreen)", si->screenitemid);
			continue;
		}

		zbx_vector_ptr_create(&widget_fields);

		DBpatch_widget_from_screen_item(si, &w, &widget_fields);

		ret = DBpatch_add_widget(id, &w, &widget_fields);

		DBpatch_trace_widget(&w);

		zbx_vector_ptr_clear_ext(&widget_fields, (zbx_clean_func_t)DBpatch_widget_field_free);
		zbx_vector_ptr_destroy(&widget_fields);
		zbx_free(w.name);
		zbx_free(w.type);
	}

	if (screen_items.values_num > 0)
	{
		lw_array_free(dim_x);
		lw_array_free(dim_y);
	}

	zbx_vector_ptr_clear_ext(&screen_items, (zbx_clean_func_t)DBpatch_screen_item_free);
	zbx_vector_ptr_destroy(&screen_items);

	return ret;
}

static int	DBpatch_convert_screen(uint64_t screenid, char *name, uint64_t userid, int private)
{
	zbx_db_result_t		result;
	int			ret;
	zbx_db_dashboard_t	dashboard;
	zbx_db_dashboard_page_t	dashboard_page;

	result = zbx_db_select(
			"select screenitemid,screenid,resourcetype,resourceid,width,height,x,y,colspan,rowspan"
			",elements,style,url,sort_triggers,application,dynamic from screens_items"
			" where screenid=" ZBX_FS_UI64, screenid);

	if (NULL == result)
		return FAIL;

	if (SUCCEED != DBpatch_init_dashboard(&dashboard, name, userid, private))
	{
		zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen '%s'due to name collision.", name);
		ret = FAIL;
		goto out;
	}

	ret = DBpatch_add_dashboard(&dashboard);

	if (SUCCEED == ret)
		ret = DBpatch_add_dashboard_page(&dashboard_page, dashboard.dashboardid, "", 0, 0);

	if (SUCCEED == ret)
		ret = DBpatch_convert_screen_items(result, dashboard_page.dashboard_pageid);

	if (SUCCEED == ret)
		ret = DBpatch_set_permissions_screen(dashboard.dashboardid, screenid);

	zbx_free(dashboard.name);
out:
	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_delay_routine(const char *screen_delay, int *dashboard_delay)
{
	int	delays[] = {10, 30, 60, 120, 600, 1800, 3600};
	int	i, imax, tmp;

	if (FAIL == zbx_is_time_suffix(screen_delay, &tmp, ZBX_LENGTH_UNLIMITED))
		return FAIL;

	imax = (int)ARRSIZE(delays);

	if (0 >= tmp)
	{
		tmp = 0;
	}
	else if (tmp <= delays[0])
	{
		tmp = delays[0];
	}
	else if (tmp >= delays[imax - 1])
	{
		tmp = delays[imax - 1];
	}
	else
	{
		for (i = 0; i < imax - 1; i++)
		{
			if (tmp >= delays[i] && tmp <= delays[i + 1])
				tmp = ((tmp - delays[i]) >= (delays[i + 1] - tmp)) ? delays[i + 1] : delays[i];
		}
	}

	*dashboard_delay = tmp;

	return SUCCEED;
}

static int	DBpatch_convert_slideshow(uint64_t slideshowid, char *name, int delay, uint64_t userid, int private)
{
	int			ret;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_db_dashboard_t	dashboard;
	zbx_db_result_t		result;
	zbx_db_row_t		row;

	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
			"select slideid,screenid,step,delay"
			" from slides"
			" where slideshowid=" ZBX_FS_UI64
			" order by step asc", slideshowid);

	result = zbx_db_select_n(sql, 50);
	zbx_free(sql);

	if (NULL == result)
		return FAIL;

	if (SUCCEED != DBpatch_init_dashboard(&dashboard, name, userid, private))
	{
		zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen '%s'due to name collision.", name);
		ret = FAIL;
		goto exit;
	}

	dashboard.display_period = delay;

	if (SUCCEED != (ret = DBpatch_add_dashboard(&dashboard)))
		goto out;

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		int			step, page_delay;
		zbx_db_dashboard_page_t	dashboard_page;
		zbx_db_result_t		result2, result3;
		zbx_db_row_t		row2;
		uint64_t 		screenid;

		step = atoi(row[2]);
		page_delay = atoi(row[3]);
		ZBX_DBROW2UINT64(screenid, row[1]);

		result2 = zbx_db_select("select name from screens where screenid=" ZBX_FS_UI64, screenid);

		if (NULL == result2 || NULL == (row2 = zbx_db_fetch(result2)))
		{
			zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen " ZBX_FS_UI64, screenid);
			zbx_db_free_result(result2);
			continue;
		}

		if (SUCCEED != (ret = DBpatch_add_dashboard_page(&dashboard_page, dashboard.dashboardid,
				row2[0], page_delay, step)))
		{
			zabbix_log(LOG_LEVEL_ERR, "Cannot convert screen " ZBX_FS_UI64, screenid);
			zbx_db_free_result(result2);
			continue;
		}

		result3 = zbx_db_select(
			"select screenitemid,screenid,resourcetype,resourceid,width,height,x,y,colspan,rowspan"
			",elements,style,url,sort_triggers,application,dynamic from screens_items"
			" where screenid=" ZBX_FS_UI64, screenid);

		if (NULL != result3)
			DBpatch_convert_screen_items(result3, dashboard_page.dashboard_pageid);

		zbx_db_free_result(result2);
		zbx_db_free_result(result3);
	}

out:
	ret = DBpatch_set_permissions_slideshow(dashboard.dashboardid, slideshowid);

	zbx_free(dashboard.name);
exit:
	zbx_db_free_result(result);

	return ret;
}

#undef OFFSET_ARRAY_SIZE

static int	DBpatch_5030094(void)
{
	int		ret = SUCCEED;
	zbx_db_result_t	result;
	zbx_db_row_t	row;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select("select slideshowid,name,delay,userid,private from slideshows");

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		uint64_t	slideshowid, userid;
		int		private, delay;

		ZBX_DBROW2UINT64(slideshowid, row[0]);
		ZBX_DBROW2UINT64(userid, row[3]);
		private = atoi(row[4]);

		if (FAIL == (ret = DBpatch_delay_routine(row[2], &delay)))
			break;

		ret = DBpatch_convert_slideshow(slideshowid, row[1], delay, userid, private);
	}

	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_5030095(void)
{
	int		ret = SUCCEED;
	zbx_db_result_t	result;
	zbx_db_row_t	row;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select("select screenid,name,userid,private from screens");

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		uint64_t	screenid, userid;
		int		private;

		ZBX_DBROW2UINT64(screenid, row[0]);
		ZBX_DBROW2UINT64(userid, row[2]);
		private = atoi(row[3]);

		if (SUCCEED == (ret = DBpatch_convert_screen(screenid, row[1], userid, private)))
			ret = DBpatch_delete_screen(screenid);
	}

	zbx_db_free_result(result);

	return ret;
}

#undef DASHBOARD_MAX_COLS
#undef DASHBOARD_MAX_ROWS
#undef DASHBOARD_WIDGET_MIN_ROWS
#undef DASHBOARD_WIDGET_MAX_ROWS

#undef SCREEN_MAX_ROWS
#undef SCREEN_MAX_COLS
#undef SCREEN_RESOURCE_CLOCK
#undef SCREEN_RESOURCE_GRAPH
#undef SCREEN_RESOURCE_SIMPLE_GRAPH
#undef SCREEN_RESOURCE_LLD_GRAPH
#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
#undef SCREEN_RESOURCE_PLAIN_TEXT
#undef SCREEN_RESOURCE_URL

#undef SCREEN_RESOURCE_MAP
#undef SCREEN_RESOURCE_HOST_INFO
#undef SCREEN_RESOURCE_TRIGGER_INFO
#undef SCREEN_RESOURCE_SERVER_INFO
#undef SCREEN_RESOURCE_TRIGGER_OVERVIEW
#undef SCREEN_RESOURCE_DATA_OVERVIEW
#undef SCREEN_RESOURCE_ACTIONS
#undef SCREEN_RESOURCE_EVENTS
#undef SCREEN_RESOURCE_HOSTGROUP_TRIGGERS
#undef SCREEN_RESOURCE_SYSTEM_STATUS
#undef SCREEN_RESOURCE_HOST_TRIGGERS

#undef ZBX_WIDGET_FIELD_TYPE_INT32
#undef ZBX_WIDGET_FIELD_TYPE_STR
#undef ZBX_WIDGET_FIELD_TYPE_ITEM
#undef ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE

/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */

#undef ZBX_WIDGET_TYPE_CLOCK
#undef ZBX_WIDGET_TYPE_GRAPH_CLASSIC
#undef ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE
#undef ZBX_WIDGET_TYPE_PLAIN_TEXT
#undef ZBX_WIDGET_TYPE_URL
#undef POS_EMPTY
#undef POS_TAKEN
#undef SKIP_EMPTY

static int	DBpatch_5030096(void)
{
	return DBdrop_table("slides");
}

static int	DBpatch_5030097(void)
{
	return DBdrop_table("slideshow_user");
}

static int	DBpatch_5030098(void)
{
	return DBdrop_table("slideshow_usrgrp");
}

static int	DBpatch_5030099(void)
{
	return DBdrop_table("slideshows");

}

static int	DBpatch_5030100(void)
{
	return DBdrop_table("screen_usrgrp");
}

static int	DBpatch_5030101(void)
{
	return DBdrop_table("screens_items");
}

static int	DBpatch_5030102(void)
{
	return DBdrop_table("screen_user");
}

static int	DBpatch_5030103(void)
{
	return DBdrop_table("screens");
}

static int	DBpatch_5030104(void)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from widget where type='favscreens'"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030105(void)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx in ("
			"'web.favorite.screenids', "
			"'web.screenconf.filter.active', "
			"'web.screenconf.filter_name', "
			"'web.screenconf.php.sort', "
			"'web.screenconf.php.sortorder', "
			"'web.screens.elementid', "
			"'web.screens.filter.active', "
			"'web.screens.filter.from', "
			"'web.screens.filter.to', "
			"'web.screens.hostid', "
			"'web.screens.tr_groupid', "
			"'web.screens.tr_hostid', "
			"'web.slideconf.filter.active', "
			"'web.slideconf.filter_name', "
			"'web.slideconf.php.sort', "
			"'web.slideconf.php.sortorder', "
			"'web.slides.elementid', "
			"'web.slides.filter.active', "
			"'web.slides.filter.from', "
			"'web.slides.filter.to', "
			"'web.slides.hostid', "
			"'web.slides.rf_rate.hat_slides', "
			"'web.favorite.screenids')"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030106(void)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from role_rule"
			" where name like 'api.method.%%'"
			" and (value_str like 'screen.%%' or value_str like 'screenitem.%%')"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030107(void)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from role_rule where name='ui.monitoring.screens'"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030108(void)
{
	int		i;
	const char	*values[] = {
			"web.dashbrd.dashboardid", "web.dashboard.dashboardid",
			"web.dashbrd.hostid", "web.dashboard.hostid",
			"web.dashbrd.list.sort", "web.dashboard.list.sort",
			"web.dashbrd.list.sortorder", "web.dashboard.list.sortorder",
			"web.dashbrd.list_was_opened", "web.dashboard.list_was_opened",
			"web.dashbrd.filter", "web.dashboard.filter",
			"web.dashbrd.filter.active", "web.dashboard.filter.active",
			"web.dashbrd.filter.from", "web.dashboard.filter.from",
			"web.dashbrd.filter.to", "web.dashboard.filter.to",
			"web.dashbrd.filter_name", "web.dashboard.filter_name",
			"web.dashbrd.filter_show", "web.dashboard.filter_show",
			"web.dashbrd.last_widget_type", "web.dashboard.last_widget_type",
			"web.dashbrd.navtree.item.selected", "web.dashboard.widget.navtree.item.selected",
			"web.dashbrd.widget.rf_rate", "web.dashboard.widget.rf_rate",
			"web.templates.dashbrd.list.sort", "web.templates.dashboard.list.sort",
			"web.templates.dashbrd.list.sortorder", "web.templates.dashboard.list.sortorder"
		};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 0; i < (int)ARRSIZE(values); i += 2)
	{
		if (ZBX_DB_OK > zbx_db_execute("update profiles set idx='%s' where idx='%s'", values[i + 1], values[i]))
			return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030109(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update profiles set idx=CONCAT('web.dashboard.widget.navtree.item-', SUBSTR(idx, 21))"
			" where idx like 'web.dashbrd.navtree-%%.toggle'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030110(void)
{
	const zbx_db_table_t	table =
			{"item_tag", "itemtagid", 0,
				{
					{"itemtagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"itemid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030111(void)
{
	return DBcreate_index("item_tag", "item_tag_1", "itemid", 0);
}

static int	DBpatch_5030112(void)
{
	const zbx_db_field_t	field = {"itemid", NULL, "items", "itemid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("item_tag", 1, &field);
}

static int	DBpatch_5030113(void)
{
	const zbx_db_table_t table =
			{"httptest_tag", "httptesttagid", 0,
				{
					{"httptesttagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"httptestid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030114(void)
{
	return DBcreate_index("httptest_tag", "httptest_tag_1", "httptestid", 0);
}

static int	DBpatch_5030115(void)
{
	const zbx_db_field_t	field = {"httptestid", NULL, "httptest", "httptestid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("httptest_tag", 1, &field);
}

static int	DBpatch_5030116(void)
{
	const zbx_db_table_t	table =
			{"sysmaps_element_tag", "selementtagid", 0,
				{
					{"selementtagid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"selementid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"tag", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"operator", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030117(void)
{
	return DBcreate_index("sysmaps_element_tag", "sysmaps_element_tag_1", "selementid", 0);
}

static int	DBpatch_5030118(void)
{
	const zbx_db_field_t	field = {"selementid", NULL, "sysmaps_elements", "selementid", 0, 0, 0,
			ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("sysmaps_element_tag", 1, &field);
}

static int	DBpatch_5030119(void)
{
	const zbx_db_field_t	field = {"evaltype", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("sysmaps_elements", &field);
}

static int	DBpatch_5030120(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	zbx_uint64_t	itemid, itemtagid = 1;
	int		ret;
	char		*value;
	zbx_db_insert_t	db_insert;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", (char *)NULL);
	result = zbx_db_select(
			"select i.itemid,a.name from items i"
			" join items_applications ip on i.itemid=ip.itemid"
			" join applications a on ip.applicationid=a.applicationid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_DBROW2UINT64(itemid, row[0]);
		value = zbx_db_dyn_escape_string(row[1]);
		zbx_db_insert_add_values(&db_insert, itemtagid++, itemid, "Application", value);
		zbx_free(value);
	}
	zbx_db_free_result(result);

	ret = zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	return ret;
}

static int	DBpatch_5030121(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	zbx_uint64_t	itemid;
	int		ret;
	char		*value;
	zbx_db_insert_t	db_insert;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "item_tag", "itemtagid", "itemid", "tag", "value", (char *)NULL);

	result = zbx_db_select(
			"select i.itemid,ap.name from items i"
			" join item_application_prototype ip on i.itemid=ip.itemid"
			" join application_prototype ap on ip.application_prototypeid=ap.application_prototypeid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_DBROW2UINT64(itemid, row[0]);
		value = zbx_db_dyn_escape_string(row[1]);
		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), itemid, "Application", value);
		zbx_free(value);
	}
	zbx_db_free_result(result);

	zbx_db_insert_autoincrement(&db_insert, "itemtagid");
	ret = zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	return ret;
}

static int	DBpatch_5030122(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	zbx_uint64_t	httptestid, httptesttagid = 1;
	int		ret;
	char		*value;
	zbx_db_insert_t	db_insert;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "httptest_tag", "httptesttagid", "httptestid", "tag", "value", (char *)NULL);
	result = zbx_db_select(
			"select h.httptestid,a.name from httptest h"
			" join applications a on h.applicationid=a.applicationid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_DBROW2UINT64(httptestid, row[0]);
		value = zbx_db_dyn_escape_string(row[1]);
		zbx_db_insert_add_values(&db_insert, httptesttagid++, httptestid, "Application", value);
		zbx_free(value);
	}
	zbx_db_free_result(result);

	ret = zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	return ret;
}

static int	DBpatch_5030123(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	zbx_uint64_t	selementid, selementtagid = 1;
	int		ret;
	char		*value;
	zbx_db_insert_t	db_insert;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "sysmaps_element_tag", "selementtagid", "selementid", "tag", "value",
			(char *)NULL);
	result = zbx_db_select(
			"select selementid,application from sysmaps_elements"
			" where elementtype in (0,3) and application<>''");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_DBROW2UINT64(selementid, row[0]);
		value = zbx_db_dyn_escape_string(row[1]);
		zbx_db_insert_add_values(&db_insert, selementtagid++, selementid, "Application", value);
		zbx_free(value);
	}
	zbx_db_free_result(result);

	ret = zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	return ret;
}

static int	DBpatch_5030127(void)
{
#define ZBX_CONDITION_TYPE_APPLICATION	15 /* deprecated */
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update conditions set conditiontype=%d,value2='Application' where conditiontype=%d",
			ZBX_CONDITION_TYPE_EVENT_TAG_VALUE, ZBX_CONDITION_TYPE_APPLICATION))
	{
		return FAIL;
	}

	return SUCCEED;
#undef ZBX_CONDITION_TYPE_APPLICATION
}

static int	DBpatch_5030128(void)
{
#define ZBX_AUDIT_RESOURCE_APPLICATION	12
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from auditlog where resourcetype=%d", ZBX_AUDIT_RESOURCE_APPLICATION))
		return FAIL;

	return SUCCEED;
#undef ZBX_AUDIT_RESOURCE_APPLICATION
}

static int	DBpatch_5030129(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx in ("
			"'web.applications.filter','web.latest.filter.application',"
			"'web.overview.filter.application','web.applications.filter.active',"
			"'web.applications.filter_groups','web.applications.filter_hostids',"
			"'web.applications.php.sort','web.applications.php.sortorder',"
			"'web.hosts.items.subfilter_apps','web.templates.items.subfilter_apps',"
			"'web.latest.toggle','web.latest.toggle_other','web.items.filter_application')"))
		return FAIL;

	return SUCCEED;
}

typedef struct
{
	char	*tag;
	char	*op;
	char	*value;
}
patch_filtertag_t;

ZBX_PTR_VECTOR_DECL(patch_filtertag, patch_filtertag_t)
ZBX_PTR_VECTOR_IMPL(patch_filtertag, patch_filtertag_t)

static void	patch_filtertag_free(patch_filtertag_t tag)
{
	zbx_free(tag.tag);
	zbx_free(tag.op);
	zbx_free(tag.value);
}

static int	DBpatch_parse_tags_json(struct zbx_json_parse *jp, zbx_vector_patch_filtertag_t *tags)
{
	const char		*p = NULL;
	struct zbx_json_parse	jp_data;
	patch_filtertag_t	tag;
	size_t			tag_alloc, op_alloc, val_alloc;

	while (NULL != (p = zbx_json_next(jp, p)))
	{
		if (SUCCEED != zbx_json_brackets_open(p, &jp_data))
			return FAIL;

		tag.tag = NULL;
		tag.op = NULL;
		tag.value = NULL;

		tag_alloc = 0;
		op_alloc = 0;
		val_alloc = 0;

		if (SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "tag", &tag.tag, &tag_alloc, NULL) ||
				SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "operator", &tag.op, &op_alloc, NULL) ||
				SUCCEED != zbx_json_value_by_name_dyn(&jp_data, "value", &tag.value, &val_alloc, NULL))
		{
			patch_filtertag_free(tag);
			return FAIL;
		}

		zbx_vector_patch_filtertag_append(tags, tag);
	}

	return SUCCEED;
}

static int	DBpatch_parse_applications_json(struct zbx_json_parse *jp, struct zbx_json *json_dest,
		zbx_vector_patch_filtertag_t *tags, char **app, int depth)
{
	struct zbx_json_parse	jp_sub;
	const char		*p = NULL, *prev = NULL;
	char			name[MAX_STRING_LEN], value[MAX_STRING_LEN];
	zbx_json_type_t		type;

	do
	{
		if (NULL != (p = zbx_json_pair_next(jp, p, name, sizeof(name))))
		{
			if (NULL == zbx_json_decodevalue(p, value, sizeof(value), NULL))
			{
				type = zbx_json_valuetype(p);

				if (type == ZBX_JSON_TYPE_ARRAY)
				{
					if (0 == depth && 0 == strcmp(name, "tags"))
					{
						if (SUCCEED != zbx_json_brackets_open(p, &jp_sub) ||
								SUCCEED != DBpatch_parse_tags_json(&jp_sub, tags))
						{
							return FAIL;
						}

						continue;
					}

					zbx_json_addarray(json_dest, name);
				}
				else if (type == ZBX_JSON_TYPE_OBJECT)
				{
					zbx_json_addobject(json_dest, name);
				}
			}
			else
			{
				if (0 == depth && 0 == strcmp(name, "application"))
					*app = zbx_strdup(*app, value);
				else
					zbx_json_addstring(json_dest, name, value, ZBX_JSON_TYPE_STRING);
			}
		}
		else
		{
			p = prev;

			if (NULL == (p = zbx_json_next_value(jp, p, value, sizeof(value), NULL)))
			{
				p = prev;

				if (NULL != (p = zbx_json_next(jp, p)))
				{
					type = zbx_json_valuetype(p);

					if (type == ZBX_JSON_TYPE_OBJECT)
						zbx_json_addobject(json_dest, NULL);
					else if (type == ZBX_JSON_TYPE_ARRAY)
						zbx_json_addarray(json_dest, NULL);
				}
				else
				{
					if (0 == depth)
					{
						if (NULL != *app)
						{
							patch_filtertag_t	tag;

							tag.tag = zbx_strdup(NULL, "Application");
							tag.op = zbx_strdup(NULL, "0");
							tag.value = zbx_strdup(NULL, *app);

							zbx_vector_patch_filtertag_append(tags, tag);
						}

						if (0 < tags->values_num)
						{
							int	i;

							zbx_json_addarray(json_dest, "tags");

							for (i = 0; i < tags->values_num; i++)
							{
								zbx_json_addobject(json_dest, NULL);
								zbx_json_addstring(json_dest, "tag",
										tags->values[i].tag,
										ZBX_JSON_TYPE_STRING);
								zbx_json_addstring(json_dest, "operator",
										tags->values[i].op,
										ZBX_JSON_TYPE_STRING);
								zbx_json_addstring(json_dest, "value",
										tags->values[i].value,
										ZBX_JSON_TYPE_STRING);
								zbx_json_close(json_dest);
							}
						}

						zbx_json_close(json_dest);
					}

					zbx_json_close(json_dest);
				}
			}
			else
				zbx_json_addstring(json_dest, NULL, value, ZBX_JSON_TYPE_STRING);
		}

		if (NULL != p && SUCCEED == zbx_json_brackets_open(p, &jp_sub))
		{
			if (SUCCEED != DBpatch_parse_applications_json(&jp_sub, json_dest, tags, app, depth + 1))
				return FAIL;
		}

		prev = p;
	} while (NULL != p);

	return SUCCEED;
}

static int	DBpatch_5030130(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	char		*sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	result = zbx_db_select("select profileid,value_str from profiles"
			" where idx='web.monitoring.problem.properties'");

	while (NULL != (row = zbx_db_fetch(result)) && SUCCEED == ret)
	{
		struct zbx_json_parse		jp;
		struct zbx_json			json;
		zbx_vector_patch_filtertag_t	tags;
		char				*app, *value_str;
		zbx_uint64_t			profileid;

		app = NULL;
		zbx_vector_patch_filtertag_create(&tags);
		zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);

		if (SUCCEED == (ret = zbx_json_open(row[1], &jp)) &&
				SUCCEED == (ret = DBpatch_parse_applications_json(&jp, &json, &tags, &app, 0)))
		{
			ZBX_DBROW2UINT64(profileid, row[0]);

			value_str = zbx_db_dyn_escape_string(json.buffer);
			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
					"update profiles set value_str='%s' where profileid=" ZBX_FS_UI64 ";\n",
					value_str, profileid);
			zbx_free(value_str);

			ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
		}
		else
			zabbix_log(LOG_LEVEL_ERR, "failed to parse web.monitoring.problem.properties JSON");

		zbx_vector_patch_filtertag_clear_ext(&tags, patch_filtertag_free);
		zbx_vector_patch_filtertag_destroy(&tags);
		zbx_free(app);
		zbx_json_free(&json);
	}
	zbx_db_free_result(result);

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;

	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030131(void)
{
	zbx_db_row_t		row;
	zbx_db_result_t		result;
	zbx_db_insert_t		db_insert;
	zbx_vector_uint64_t	widget_fieldids;
	int			ret;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "widget_field", "widget_fieldid", "widgetid", "type", "name", "value_int",
			"value_str", (char *)NULL);

	zbx_vector_uint64_create(&widget_fieldids);

	result = zbx_db_select(
			"select w.widgetid,wf.value_str,wf.widget_fieldid from widget w"
			" join widget_field wf on wf.widgetid=w.widgetid"
			" where w.type in ('dataover','trigover') and wf.type=1 and wf.name='application'");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_uint64_t	widgetid, widget_fieldid;
		char		*val;

		ZBX_DBROW2UINT64(widgetid, row[0]);
		val = zbx_db_dyn_escape_string(row[1]);
		ZBX_DBROW2UINT64(widget_fieldid, row[2]);

		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 0, "tags.operator.0", 0, "");
		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 1, "tags.tag.0", 0, "Application");
		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), widgetid, 1, "tags.value.0", 0, val);

		zbx_vector_uint64_append(&widget_fieldids, widget_fieldid);

		zbx_free(val);
	}
	zbx_db_free_result(result);

	zbx_db_insert_autoincrement(&db_insert, "widget_fieldid");

	if (SUCCEED != (ret = zbx_db_insert_execute(&db_insert)))
		goto out;

	if (0 < widget_fieldids.values_num)
	{
		char	*sql = NULL;
		size_t	sql_alloc = 0, sql_offset = 0;

		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "delete from widget_field where");
		zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "widget_fieldid", widget_fieldids.values,
				widget_fieldids.values_num);

		if (ZBX_DB_OK > zbx_db_execute("%s", sql))
			ret = FAIL;

		zbx_free(sql);
	}
out:
	zbx_db_insert_clean(&db_insert);
	zbx_vector_uint64_destroy(&widget_fieldids);

	return ret;
}

static int	DBpatch_5030132(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from role_rule where name like 'api.method.%%'"
			" and value_str like 'application.%%'"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5030133(void)
{
	return DBdrop_foreign_key("httptest", 1);
}

static int	DBpatch_5030134(void)
{
	return DBdrop_index("httptest", "httptest_1");
}

static int	DBpatch_5030135(void)
{
	return DBdrop_field("httptest", "applicationid");
}

static int	DBpatch_5030136(void)
{
	return DBdrop_field("sysmaps_elements", "application");
}

static int	DBpatch_5030137(void)
{
	return DBdrop_table("application_discovery");
}

static int	DBpatch_5030138(void)
{
	return DBdrop_table("item_application_prototype");
}

static int	DBpatch_5030139(void)
{
	return DBdrop_table("application_prototype");
}

static int	DBpatch_5030140(void)
{
	return DBdrop_table("application_template");
}

static int	DBpatch_5030141(void)
{
	return DBdrop_table("items_applications");
}

static int	DBpatch_5030142(void)
{
	return DBdrop_table("applications");
}

static int	DBpatch_5030143(void)
{
	zbx_db_result_t		result;
	int			ret;
	zbx_field_len_t		fields[] = {
			{"subject", 255},
			{"message", 65535}
	};

	result = zbx_db_select("select om.operationid,om.subject,om.message"
			" from opmessage om,operations o,actions a"
			" where om.operationid=o.operationid"
				" and o.actionid=a.actionid"
				" and a.eventsource=0 and o.operationtype=11");

	ret = db_rename_macro(result, "opmessage", "operationid", fields, ARRSIZE(fields), "{EVENT.NAME}",
			"{EVENT.RECOVERY.NAME}");

	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_5030144(void)
{
	zbx_db_result_t		result;
	int			ret;
	zbx_field_len_t		fields[] = {
			{"subject", 255},
			{"message", 65535}
	};

	result = zbx_db_select("select mediatype_messageid,subject,message from media_type_message where recovery=1");

	ret = db_rename_macro(result, "media_type_message", "mediatype_messageid", fields, ARRSIZE(fields),
			"{EVENT.NAME}", "{EVENT.RECOVERY.NAME}");

	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_5030145(void)
{
	const zbx_db_table_t	table =
			{"report", "reportid", 0,
				{
					{"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"description", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"status", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"dashboardid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"period", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"cycle", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"weekdays", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"start_time", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"active_since", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"active_till", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"state", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"lastsent", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"info", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030146(void)
{
	return DBcreate_index("report", "report_1", "name", 1);
}

static int	DBpatch_5030147(void)
{
	const zbx_db_field_t	field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report", 1, &field);
}

static int	DBpatch_5030148(void)
{
	const zbx_db_field_t	field = {"dashboardid", NULL, "dashboard", "dashboardid", 0, 0, 0,
			ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report", 2, &field);
}

static int	DBpatch_5030149(void)
{
	const zbx_db_table_t	table =
			{"report_param", "reportparamid", 0,
				{
					{"reportparamid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030150(void)
{
	return DBcreate_index("report_param", "report_param_1", "reportid", 0);
}

static int	DBpatch_5030151(void)
{
	const zbx_db_field_t	field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report_param", 1, &field);
}

static int	DBpatch_5030152(void)
{
	const zbx_db_table_t	table =
			{"report_user", "reportuserid", 0,
				{
					{"reportuserid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"exclude", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"access_userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030153(void)
{
	return DBcreate_index("report_user", "report_user_1", "reportid", 0);
}

static int	DBpatch_5030154(void)
{
	const zbx_db_field_t	field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report_user", 1, &field);
}

static int	DBpatch_5030155(void)
{
	const zbx_db_field_t	field = {"userid", NULL, "users", "userid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report_user", 2, &field);
}

static int	DBpatch_5030156(void)
{
	const zbx_db_field_t	field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0};

	return DBadd_foreign_key("report_user", 3, &field);
}

static int	DBpatch_5030157(void)
{
	const zbx_db_table_t	table =
			{"report_usrgrp", "reportusrgrpid", 0,
				{
					{"reportusrgrpid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"reportid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"usrgrpid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"access_userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030158(void)
{
	return DBcreate_index("report_usrgrp", "report_usrgrp_1", "reportid", 0);
}

static int	DBpatch_5030159(void)
{
	const zbx_db_field_t	field = {"reportid", NULL, "report", "reportid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report_usrgrp", 1, &field);
}

static int	DBpatch_5030160(void)
{
	const zbx_db_field_t	field = {"usrgrpid", NULL, "usrgrp", "usrgrpid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("report_usrgrp", 2, &field);
}

static int	DBpatch_5030161(void)
{
	const zbx_db_field_t	field = {"access_userid", NULL, "users", "userid", 0, 0, 0, 0};

	return DBadd_foreign_key("report_usrgrp", 3, &field);
}

static int	DBpatch_5030162(void)
{
	const zbx_db_field_t	field = {"url", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5030163(void)
{
	const zbx_db_field_t	field = {"report_test_timeout", "60s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5030164(void)
{
	const zbx_db_field_t	field = {"dbversion_status", "", NULL, NULL, 1024, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

/* trigger function conversion to new syntax */

#define ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION		0x01
#define ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION	0x02

#define ZBX_DBPATCH_TRIGGER_UPDATE			(ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION | \
							ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION)

ZBX_VECTOR_DECL(loc, zbx_strloc_t)
ZBX_VECTOR_IMPL(loc, zbx_strloc_t)

typedef struct
{
	zbx_uint64_t	triggerid;
	unsigned char	recovery_mode;
	unsigned char	flags;
	char		*expression;
	char		*recovery_expression;
}
zbx_dbpatch_trigger_t;

static void	dbpatch_trigger_clear(zbx_dbpatch_trigger_t *trigger)
{
	zbx_free(trigger->expression);
	zbx_free(trigger->recovery_expression);
}

/******************************************************************************
 *                                                                            *
 * Purpose: replace {functionid} occurrences in expression with the specified *
 *          replacement string                                                *
 *                                                                            *
 * Return value: SUCCEED - expression was changed                             *
 *               FAIL - otherwise                                             *
 *                                                                            *
 ******************************************************************************/
static int	dbpatch_update_expression(char **expression, zbx_uint64_t functionid, const char *replace)
{
	int		pos = 0, last_pos = 0;
	zbx_token_t	token;
	char		*out = NULL;
	size_t		out_alloc = 0, out_offset = 0;
	zbx_uint64_t	id;

	for (; SUCCEED == zbx_token_find(*expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID |
			ZBX_TOKEN_SEARCH_SIMPLE_MACRO); pos++)
	{
		switch (token.type)
		{
			case ZBX_TOKEN_OBJECTID:
				if (SUCCEED == zbx_is_uint64_n(*expression + token.data.objectid.name.l,
						token.data.objectid.name.r - token.data.objectid.name.l + 1, &id) &&
						functionid == id)
				{
					zbx_strncpy_alloc(&out, &out_alloc, &out_offset,
							*expression + last_pos, token.loc.l - last_pos);
					zbx_strcpy_alloc(&out, &out_alloc, &out_offset, replace);
					last_pos = token.loc.r + 1;
				}
				pos = token.loc.r;
				break;
			case ZBX_TOKEN_MACRO:
			case ZBX_TOKEN_USER_MACRO:
			case ZBX_TOKEN_LLD_MACRO:
				pos = token.loc.r;
				break;
		}
	}

	if (NULL == out)
		return FAIL;

	zbx_strcpy_alloc(&out, &out_alloc, &out_offset, *expression + last_pos);

	zbx_free(*expression);
	*expression = out;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: replace {functionid} occurrences in trigger expression and        *
 *          recovery expression with the specified replacement string         *
 *                                                                            *
 ******************************************************************************/
static void	dbpatch_update_trigger(zbx_dbpatch_trigger_t *trigger, zbx_uint64_t functionid, const char *replace)
{
	if (SUCCEED == dbpatch_update_expression(&trigger->expression, functionid, replace))
		trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION;

	if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode)
	{
		if (SUCCEED == dbpatch_update_expression(&trigger->recovery_expression, functionid, replace))
			trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION;
	}
}

#define ZBX_DBPATCH_EXPRESSION			0x01
#define ZBX_DBPATCH_RECOVERY_EXPRESSION		0x02

/******************************************************************************
 *                                                                            *
 * Purpose: check if the expression contains specified functionid             *
 *                                                                            *
 ******************************************************************************/
static int	dbpatch_find_function(const char *expression, zbx_uint64_t functionid)
{
	int		pos = 0;
	zbx_token_t	token;
	zbx_uint64_t	id;

	for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID |
			ZBX_TOKEN_SEARCH_SIMPLE_MACRO); pos++)
	{
		switch (token.type)
		{
			case ZBX_TOKEN_OBJECTID:
				if (SUCCEED == zbx_is_uint64_n(expression + token.data.objectid.name.l,
						token.data.objectid.name.r - token.data.objectid.name.l + 1, &id) &&
						functionid == id)
				{
					return SUCCEED;
				}
				pos = token.loc.r;
				break;
			case ZBX_TOKEN_MACRO:
			case ZBX_TOKEN_USER_MACRO:
			case ZBX_TOKEN_LLD_MACRO:
				pos = token.loc.r;
				break;
		}
	}

	return FAIL;
}

/******************************************************************************
 *                                                                            *
 * Purpose: return function location mask (expression | recovery expression)  *
 *                                                                            *
 ******************************************************************************/
static unsigned char	dbpatch_get_function_location(const zbx_dbpatch_trigger_t *trigger, zbx_uint64_t functionid)
{
	unsigned char	mask = 0;

	if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION != trigger->recovery_mode)
		return ZBX_DBPATCH_EXPRESSION;

	if (SUCCEED == dbpatch_find_function(trigger->expression, functionid))
		mask |= ZBX_DBPATCH_EXPRESSION;

	if (SUCCEED == dbpatch_find_function(trigger->recovery_expression, functionid))
		mask |= ZBX_DBPATCH_RECOVERY_EXPRESSION;

	return mask;
}

/******************************************************************************
 *                                                                            *
 * Purpose: convert trigger and its functions to use new expression syntax    *
 *                                                                            *
 * Parameters: trigger   - [IN/OUT] the trigger data/updates                  *
 *             functions - [OUT] the function updates                         *
 *                                                                            *
 ******************************************************************************/
static int	dbpatch_convert_trigger(zbx_dbpatch_trigger_t *trigger, zbx_vector_ptr_t *functions)
{
	zbx_db_row_t			row;
	zbx_db_result_t			result;
	int				i, index;
	zbx_uint64_t			functionid, itemid, hostid;
	zbx_vector_loc_t		params;
	zbx_vector_ptr_t		common_functions, trigger_functions;
	zbx_vector_uint64_t		hostids, r_hostids;
	zbx_dbpatch_function_t		*func;

	index = functions->values_num;

	zbx_vector_loc_create(&params);
	zbx_vector_ptr_create(&common_functions);
	zbx_vector_ptr_create(&trigger_functions);
	zbx_vector_uint64_create(&hostids);
	zbx_vector_uint64_create(&r_hostids);

	result = zbx_db_select("select f.functionid,f.itemid,f.name,f.parameter,h.hostid"
			" from functions f"
			" join items i"
				" on f.itemid=i.itemid"
			" join hosts h"
				" on i.hostid=h.hostid"
			" where triggerid=" ZBX_FS_UI64
			" order by functionid",
			trigger->triggerid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char		*replace = NULL;
		unsigned char	location;

		ZBX_STR2UINT64(functionid, row[0]);
		ZBX_STR2UINT64(itemid, row[1]);
		ZBX_STR2UINT64(hostid, row[4]);

		if (SUCCEED == dbpatch_is_time_function(row[2], strlen(row[2])))
		{
			char	func_name[12 /* FUNCTION_NAME_LEN */ * 4 + 1];

			func = dbpatch_new_function(functionid, itemid, row[2], row[3], 0);
			func->hostid = hostid;
			func->location = dbpatch_get_function_location(trigger, functionid);
			zbx_vector_ptr_append(&common_functions, func);

			zbx_snprintf(func_name, sizeof(func_name), "%s()", row[2]);
			dbpatch_update_trigger(trigger, functionid, func_name);

			continue;
		}

		location = dbpatch_get_function_location(trigger, functionid);

		if (0 != (location & ZBX_DBPATCH_EXPRESSION))
			zbx_vector_uint64_append(&hostids, hostid);

		if (0 != (location & ZBX_DBPATCH_RECOVERY_EXPRESSION))
			zbx_vector_uint64_append(&r_hostids, hostid);

		func = dbpatch_new_function(functionid, itemid, row[2], row[3], 0);
		zbx_vector_ptr_append(functions, func);
		dbpatch_convert_function(func, &replace, functions);

		if (NULL != replace)
		{
			dbpatch_update_trigger(trigger, func->functionid, replace);
			zbx_free(replace);
		}
	}
	zbx_db_free_result(result);

	for (i = index; i < functions->values_num; i++)
	{
		func = (zbx_dbpatch_function_t *)functions->values[i];

		if (0 == (func->flags & ZBX_DBPATCH_FUNCTION_DELETE) && NULL != func->parameter)
		{
			if ('\0' != *func->parameter)
				func->parameter = zbx_dsprintf(func->parameter, "$,%s", func->parameter);
			else
				func->parameter = zbx_strdup(func->parameter, "$");

			func->flags |= ZBX_DBPATCH_FUNCTION_UPDATE_PARAM;
		}
	}

	/* ensure that with history time functions converted to common time functions */
	/* the trigger is still linked to the same hosts                              */
	if (0 != common_functions.values_num)
	{
		int	extended = 0, r_extended = 0;

		zbx_vector_uint64_sort(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
		zbx_vector_uint64_uniq(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

		zbx_vector_uint64_sort(&r_hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
		zbx_vector_uint64_uniq(&r_hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);

		for (i = 0; i < common_functions.values_num; i++)
		{
			func = (zbx_dbpatch_function_t *)common_functions.values[i];
			func->flags = ZBX_DBPATCH_FUNCTION_DELETE;

			if (0 != (func->location & ZBX_DBPATCH_EXPRESSION) &&
					(FAIL == zbx_vector_uint64_search(&hostids, func->hostid,
							ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
			{
				dbpatch_update_hist2common(func, extended, &trigger->expression);
				extended = 1;
				zbx_vector_uint64_append(&hostids, func->hostid);
				trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION;
			}

			if (0 != (func->location & ZBX_DBPATCH_RECOVERY_EXPRESSION) &&
					(FAIL == zbx_vector_uint64_search(&r_hostids, func->hostid,
							ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
			{
				dbpatch_update_hist2common(func, r_extended, &trigger->recovery_expression);
				r_extended = 1;
				zbx_vector_uint64_append(&r_hostids, func->hostid);
				trigger->flags |= ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION;
			}

			zbx_vector_ptr_append(functions, func);
		}
	}

	zbx_vector_uint64_destroy(&hostids);
	zbx_vector_uint64_destroy(&r_hostids);
	zbx_vector_ptr_destroy(&trigger_functions);
	zbx_vector_ptr_destroy(&common_functions);
	zbx_vector_loc_destroy(&params);

	if (0 != (trigger->flags & ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION))
	{
		if (zbx_strlen_utf8(trigger->expression) > 2048 /* TRIGGER_EXPRESSION_LEN */ )
		{
			zabbix_log(LOG_LEVEL_WARNING, "trigger \"" ZBX_FS_UI64 "\" expression is too long: %s",
					trigger->triggerid, trigger->expression);
			return FAIL;
		}
	}

	if (0 != (trigger->flags & ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION))
	{
		if (zbx_strlen_utf8(trigger->recovery_expression) > 2048 /* TRIGGER_EXPRESSION_LEN */)
		{
			zabbix_log(LOG_LEVEL_WARNING, "trigger \"" ZBX_FS_UI64 "\" recovery expression is too long: %s",
					trigger->triggerid, trigger->recovery_expression);
			return FAIL;
		}
	}

	return SUCCEED;
}

static int	DBpatch_5030165(void)
{
	int			i, ret = SUCCEED;
	zbx_db_row_t		row;
	zbx_db_result_t		result;
	char			*sql;
	size_t			sql_alloc = 4096, sql_offset = 0;
	zbx_db_insert_t		db_insert_functions;
	zbx_vector_ptr_t	functions;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	zbx_vector_ptr_create(&functions);

	sql = zbx_malloc(NULL, sql_alloc);

	zbx_db_insert_prepare(&db_insert_functions, "functions", "functionid", "itemid", "triggerid", "name",
			"parameter", (char *)NULL);

	result = zbx_db_select("select triggerid,recovery_mode,expression,recovery_expression from triggers"
			" order by triggerid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char			delim, *esc;
		zbx_dbpatch_trigger_t	trigger;

		ZBX_STR2UINT64(trigger.triggerid, row[0]);
		ZBX_STR2UCHAR(trigger.recovery_mode, row[1]);
		trigger.expression = zbx_strdup(NULL, row[2]);
		trigger.recovery_expression = zbx_strdup(NULL, row[3]);
		trigger.flags = 0;

		if (SUCCEED == dbpatch_convert_trigger(&trigger, &functions))
		{
			for (i = 0; i < functions.values_num; i++)
			{
				zbx_dbpatch_function_t	*func = (zbx_dbpatch_function_t *)functions.values[i];

				if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_CREATE))
				{
					zbx_db_insert_add_values(&db_insert_functions, func->functionid,
							func->itemid, trigger.triggerid, func->name, func->parameter);
					continue;
				}

				if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_DELETE))
				{
					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
							"delete from functions where functionid=" ZBX_FS_UI64 ";\n",
							func->functionid);

					if (FAIL == (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
						break;

					continue;
				}

				delim = ' ';

				zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update functions set");
				if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_UPDATE_NAME))
				{
					esc = zbx_db_dyn_escape_field("functions", "name", func->name);
					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cname='%s'", delim, esc);
					zbx_free(esc);
					delim = ',';
				}

				if (0 != (func->flags & ZBX_DBPATCH_FUNCTION_UPDATE_PARAM))
				{
					esc = zbx_db_dyn_escape_field("functions", "parameter", func->parameter);
					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cparameter='%s'", delim, esc);
					zbx_free(esc);
				}

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

				if (FAIL == (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
					break;
			}

			if (SUCCEED == ret && 0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE))
			{
				delim = ' ';
				zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set");

				if (0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE_EXPRESSION))
				{
					esc = zbx_db_dyn_escape_field("triggers", "expression", trigger.expression);
					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%cexpression='%s'", delim,
							esc);
					zbx_free(esc);
					delim = ',';
				}

				if (0 != (trigger.flags & ZBX_DBPATCH_TRIGGER_UPDATE_RECOVERY_EXPRESSION))
				{
					esc = zbx_db_dyn_escape_field("triggers", "recovery_expression",
							trigger.recovery_expression);
					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,"%crecovery_expression='%s'",
							delim, esc);
					zbx_free(esc);
				}

				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " where triggerid=" ZBX_FS_UI64
						";\n", trigger.triggerid);

				if (FAIL == (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
					break;
			}
		}

		zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free);
		dbpatch_trigger_clear(&trigger);

		if (SUCCEED != ret)
			break;
	}

	zbx_db_free_result(result);

	if (SUCCEED == ret)
	{
		if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
			ret = FAIL;
	}

	if (SUCCEED == ret)
		zbx_db_insert_execute(&db_insert_functions);

	zbx_db_insert_clean(&db_insert_functions);
	zbx_free(sql);

	zbx_vector_ptr_destroy(&functions);

	return ret;
}

static int	DBpatch_5030166(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	/* When upgrading from version 5.0 or less trigger_queue will be created later. */
	/* Only when upgrading from version 5.2 there will be trigger queue table which */
	/* must be updated.                                                             */
	if (SUCCEED != zbx_db_table_exists("trigger_queue"))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("update trigger_queue set type=4 where type=3"))
		return FAIL;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: convert simple macros in expression macro {? } to function calls  *
 *          using new expression syntax                                       *
 *                                                                            *
 * Parameters: expression - [IN] the original expression                      *
 *             loc        - [IN] the macro location within expression         *
 *             replace    - [OUT] the expression macro replacement expression *
 *                                                                            *
 * Return value: SUCCEED - expression macro was converted                     *
 *               FAIL    - expression macro does not contain simple macros    *
 *                                                                            *
 ******************************************************************************/
static int	dbpatch_convert_expression_macro(const char *expression, const zbx_strloc_t *loc, char **replace)
{
	zbx_token_t	token;
	char		*out = NULL;
	size_t		out_alloc = 0, out_offset = 0, pos = loc->l + 2, last_pos = loc->l;

	for (; SUCCEED == zbx_token_find(expression, (int)pos, &token, ZBX_TOKEN_SEARCH_BASIC |
			ZBX_TOKEN_SEARCH_SIMPLE_MACRO) && token.loc.r < loc->r; pos++)
	{
		char	*macro = NULL;

		switch (token.type)
		{
			case ZBX_TOKEN_SIMPLE_MACRO:
				dbpatch_convert_simple_macro(expression, &token.data.simple_macro, 0, &macro);
				zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos,
						token.loc.l - last_pos);
				zbx_strcpy_alloc(&out, &out_alloc, &out_offset, macro);
				zbx_free(macro);
				last_pos = token.loc.r + 1;
				pos = token.loc.r;
				break;
			case ZBX_TOKEN_MACRO:
			case ZBX_TOKEN_FUNC_MACRO:
			case ZBX_TOKEN_USER_MACRO:
			case ZBX_TOKEN_LLD_MACRO:
				pos = token.loc.r;
				break;
		}
	}

	if (0 == out_offset)
		return FAIL;

	if (last_pos <= loc->r)
		zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos, loc->r - last_pos + 1);
	*replace = out;

	return SUCCEED;
}

static int	DBpatch_5030167(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	char		*sql;
	size_t		sql_alloc = 4096, sql_offset = 0;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	sql = zbx_malloc(NULL, sql_alloc);

	result = zbx_db_select("select triggerid,event_name from triggers order by triggerid");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_token_t	token;
		char		*out = NULL;
		size_t		out_alloc = 0, out_offset = 0, pos = 0, last_pos = 0;

		for (; SUCCEED == zbx_token_find(row[1], (int)pos, &token, ZBX_TOKEN_SEARCH_EXPRESSION_MACRO |
				ZBX_TOKEN_SEARCH_SIMPLE_MACRO); pos++)
		{
			char		*replace = NULL;
			zbx_strloc_t	*loc = NULL;

			switch (token.type)
			{
				case ZBX_TOKEN_EXPRESSION_MACRO:
					loc = &token.loc;
					break;
				case ZBX_TOKEN_FUNC_MACRO:
					loc = &token.data.func_macro.macro;
					if ('?' != row[1][loc->l + 1])
					{
						pos = token.loc.r;
						continue;
					}
					break;
				case ZBX_TOKEN_MACRO:
				case ZBX_TOKEN_USER_MACRO:
				case ZBX_TOKEN_LLD_MACRO:
					pos = token.loc.r;
					continue;
				default:
					continue;
			}

			if (SUCCEED == dbpatch_convert_expression_macro(row[1], loc, &replace))
			{
				zbx_strncpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos, loc->l - last_pos);
				zbx_strcpy_alloc(&out, &out_alloc, &out_offset, replace);
				zbx_free(replace);
				last_pos = loc->r + 1;
			}
			pos = token.loc.r;
		}

		if (0 == out_alloc)
			continue;

		zbx_strcpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos);

		if (2048 /*TRIGGER_EVENT_NAME_LEN */ < zbx_strlen_utf8(out))
		{
			zabbix_log(LOG_LEVEL_WARNING, "cannot convert trigger \"%s\" event name: too long expression",
					row[0]);
		}
		else
		{
			char	*esc;

			esc = zbx_db_dyn_escape_field("triggers", "event_name", out);
			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set event_name='%s'"
					" where triggerid=%s;\n", esc, row[0]);
			zbx_free(esc);

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

		zbx_free(out);
	}
	zbx_db_free_result(result);

	if (SUCCEED == ret)
	{
		if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
			ret = FAIL;
	}

	zbx_free(sql);

	return ret;
}

static int	dbpatch_validate_key_macro(const char *key)
{
	char	*params, *macro;

	if (NULL == (macro = strchr(key, '{')))
		return SUCCEED;

	if (NULL != (params = strchr(key, '[')) && params < macro)
		return SUCCEED;

	return FAIL;
}

static char	*dbpatch_formula_to_expression(zbx_uint64_t itemid, const char *formula, zbx_vector_ptr_t *functions)
{
	zbx_dbpatch_function_t	*func;
	const char		*ptr;
	char			*exp = NULL, error[128];
	size_t			exp_alloc = 0, exp_offset = 0, pos = 0, par_l, par_r;

	for (ptr = formula; SUCCEED == zbx_function_find(ptr, &pos, &par_l, &par_r, error, sizeof(error));
			ptr += par_r + 1)
	{
		size_t	param_pos, param_len, sep_pos;
		int	quoted;

		/* copy the part of the string preceding function */
		zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, ptr, pos);

		if (SUCCEED != dbpatch_is_time_function(ptr + pos, par_l - pos))
		{
			char	*arg0, *host = NULL, *key = NULL;
			int	ret;
			size_t	arg0_len;

			zbx_function_param_parse(ptr + par_l + 1, &param_pos, &param_len, &sep_pos);

			arg0 = zbx_function_param_unquote_dyn_compat(ptr + par_l + 1 + param_pos, param_len, &quoted);
			arg0_len = strlen(arg0);
			zbx_remove_chars(arg0, "\t\n\r");
			if (strlen(arg0) != arg0_len)
			{
				zabbix_log(LOG_LEVEL_WARNING, "control characters were removed from calculated item \""
						ZBX_FS_UI64 "\" formula host:key parameter at %s", itemid, ptr);
			}

			ret = zbx_parse_host_key(arg0, &host, &key);
			zbx_free(arg0);

			if (FAIL == ret)
			{
				zbx_vector_ptr_clear_ext(functions, (zbx_clean_func_t)dbpatch_function_free);
				zbx_free(exp);
				return NULL;
			}

			if (SUCCEED != dbpatch_validate_key_macro(key))
			{
				zabbix_log(LOG_LEVEL_WARNING, "invalid key parameter \"%s\" in calculated item \""
						ZBX_FS_UI64 "\" formula: using macro within item key is not supported"
								" anymore", key, itemid);
			}

			func = (zbx_dbpatch_function_t *)zbx_malloc(NULL, sizeof(zbx_dbpatch_function_t));
			func->itemid = itemid;
			func->name = zbx_substr(ptr, pos, par_l - 1);
			func->flags = 0;

			func->arg0 = zbx_dsprintf(NULL, "/%s/%s", ZBX_NULL2EMPTY_STR(host), key);
			zbx_free(host);
			zbx_free(key);

			if (')' != ptr[par_l + 1 + sep_pos])
				func->parameter = zbx_substr(ptr, par_l + sep_pos + 2, par_r - 1);
			else
				func->parameter = zbx_strdup(NULL, "");

			func->functionid = functions->values_num;
			zbx_vector_ptr_append(functions, func);

			zbx_snprintf_alloc(&exp, &exp_alloc, &exp_offset, "{" ZBX_FS_UI64 "}", func->functionid);
		}
		else
		{
			zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, ptr + pos, par_l - pos);
			zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, "()");
		}
	}

	if (par_l <= par_r)
		zbx_strcpy_alloc(&exp, &exp_alloc, &exp_offset, ptr);

	return exp;
}

static int	DBpatch_5030168(void)
{
	zbx_db_row_t		row;
	zbx_db_result_t		result;
	zbx_vector_ptr_t	functions;
	int			i, ret = SUCCEED;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;

	zbx_vector_ptr_create(&functions);

	result = zbx_db_select("select i.itemid,i.params"
			" from items i,hosts h"
			" where i.type=15"
				" and h.hostid=i.hostid"
			" order by i.itemid");

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		zbx_uint64_t	itemid, index;
		char		*expression, *out = NULL;
		int		pos = 0, last_pos = 0;
		zbx_token_t	token;
		size_t		out_alloc = 0, out_offset = 0;

		ZBX_STR2UINT64(itemid, row[0]);
		if (NULL == (expression = dbpatch_formula_to_expression(itemid, row[1], &functions)))
		{
			zabbix_log(LOG_LEVEL_WARNING, "cannot convert calculated item \"" ZBX_FS_UI64 "\"formula",
					itemid);
			continue;
		}

		for (i = 0; i < functions.values_num; i++)
		{
			char			*replace = NULL;
			zbx_dbpatch_function_t	*func = functions.values[i];

			dbpatch_convert_function(func, &replace, &functions);
			if (NULL != replace)
			{
				dbpatch_update_expression(&expression, func->functionid, replace);
				zbx_free(replace);
			}
		}

		for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID |
				ZBX_TOKEN_SEARCH_SIMPLE_MACRO); pos++)
		{
			switch (token.type)
			{
				case ZBX_TOKEN_OBJECTID:
					if (SUCCEED == zbx_is_uint64_n(expression + token.loc.l + 1,
							token.loc.r - token.loc.l - 1, &index) &&
							(int)index < functions.values_num)
					{
						zbx_dbpatch_function_t	*func = functions.values[index];

						zbx_strncpy_alloc(&out, &out_alloc, &out_offset,
								expression + last_pos, token.loc.l - last_pos);

						zbx_snprintf_alloc(&out, &out_alloc, &out_offset, "%s(%s",
								func->name, func->arg0);
						if ('\0' != *func->parameter)
						{
							zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ',');
							zbx_strcpy_alloc(&out, &out_alloc, &out_offset, func->parameter);
						}
						zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ')');
						last_pos = token.loc.r + 1;
					}
					pos = token.loc.r;
					break;
				case ZBX_TOKEN_MACRO:
				case ZBX_TOKEN_USER_MACRO:
				case ZBX_TOKEN_LLD_MACRO:
					pos = token.loc.r;
					break;
			}
		}

		zbx_strcpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos);
		if (65535 /* ZBX_ITEM_PARAM_LEN */ < zbx_strlen_utf8(out))
		{
			zabbix_log(LOG_LEVEL_WARNING, "cannot convert calculated item \"" ZBX_FS_UI64 "\" formula:"
					" too long expression", itemid);
		}
		else
		{
			char	*esc;

			esc = zbx_db_dyn_escape_field("items", "params", out);
			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update items set params='%s' where itemid="
					ZBX_FS_UI64 ";\n", esc, itemid);
			zbx_free(esc);

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

		zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free);
		zbx_free(expression);
		zbx_free(out);
	}

	zbx_db_free_result(result);
	zbx_vector_ptr_destroy(&functions);

	if (SUCCEED == ret)
	{
		if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
			ret = FAIL;
	}

	zbx_free(sql);

	return ret;
}

static int	dbpatch_aggregate2formula(const char *itemid, const AGENT_REQUEST *request, char **str,
		size_t *str_alloc, size_t *str_offset, char **error)
{
	char	*esc;

	if (3 > request->nparam)
	{
		*error = zbx_strdup(NULL, "invalid number of parameters");
		return FAIL;
	}

	if (0 == strcmp(request->key, "grpavg"))
	{
		zbx_strcpy_alloc(str, str_alloc, str_offset, "avg");
	}
	else if (0 == strcmp(request->key, "grpmax"))
	{
		zbx_strcpy_alloc(str, str_alloc, str_offset, "max");
	}
	else if (0 == strcmp(request->key, "grpmin"))
	{
		zbx_strcpy_alloc(str, str_alloc, str_offset, "min");
	}
	else if (0 == strcmp(request->key, "grpsum"))
	{
		zbx_strcpy_alloc(str, str_alloc, str_offset, "sum");
	}
	else
	{
		*error = zbx_dsprintf(NULL, "unknown group function \"%s\"", request->key);
		return FAIL;
	}

	if (SUCCEED != dbpatch_validate_key_macro(request->params[1]))
	{
		zabbix_log(LOG_LEVEL_WARNING, "invalid key parameter \"%s\" when converting aggregate check \"%s\""
				" to calculated item: using macro within item key is not supported anymore",
				request->params[1], itemid);
	}

	zbx_rtrim(request->params[1], " ");
	zbx_snprintf_alloc(str, str_alloc, str_offset, "(%s_foreach(/*/%s?[", request->params[2], request->params[1]);

	if (REQUEST_PARAMETER_TYPE_ARRAY == get_rparam_type(request, 0))
	{
		int				i, groups_num;
		char				*group;
		zbx_request_parameter_type_t	type;

		groups_num = zbx_num_param(request->params[0]);

		for (i = 1; i <= groups_num; i++)
		{
			if (NULL == (group = zbx_get_param_dyn(request->params[0], i, &type)))
				continue;

			if ('[' != (*str)[*str_offset - 1])
				zbx_strcpy_alloc(str, str_alloc, str_offset, " or ");

			esc = zbx_dyn_escape_string(group, "\\\"");
			zbx_snprintf_alloc(str, str_alloc, str_offset, "group=\"%s\"", esc);
			zbx_free(esc);
			zbx_free(group);
		}
	}
	else
	{
		esc = zbx_dyn_escape_string(request->params[0], "\\\"");
		zbx_snprintf_alloc(str, str_alloc, str_offset, "group=\"%s\"", esc);
		zbx_free(esc);
	}

	zbx_chrcpy_alloc(str, str_alloc, str_offset, ']');

	if (4 == request->nparam && 0 != strcmp("last", request->params[2]))
	{
		zbx_chrcpy_alloc(str, str_alloc, str_offset, ',');

		if (SUCCEED == dbpatch_is_composite_constant(request->params[3]))
		{
			dbpatch_strcpy_alloc_quoted_compat(str, str_alloc, str_offset, request->params[3]);
		}
		else
		{
			zbx_strcpy_alloc(str, str_alloc, str_offset, request->params[3]);

			if (0 != isdigit((*str)[*str_offset - 1]))
				zbx_chrcpy_alloc(str, str_alloc, str_offset, 's');
		}
	}

	zbx_strcpy_alloc(str, str_alloc, str_offset, "))");
	if (65535 /* ZBX_ITEM_PARAM_LEN */ < zbx_strlen_utf8(*str))
	{
		*error = zbx_strdup(NULL, "resulting formula is too long");
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5030169(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	int		ret = SUCCEED;
	char		*sql = NULL, *params = NULL;
	size_t		sql_alloc = 0, sql_offset = 0, params_alloc = 0, params_offset;

	/* ITEM_TYPE_AGGREGATE = 8 */
	result = zbx_db_select("select itemid,key_ from items where type=8");

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		AGENT_REQUEST	request;
		char		*error = NULL, *esc;
		int		ret_formula;

		params_offset = 0;

		zbx_init_agent_request(&request);

		if (SUCCEED != zbx_parse_item_key(row[1], &request))
		{
			zabbix_log(LOG_LEVEL_WARNING, "Cannot parse aggregate checks item key \"%s\"", row[1]);
			continue;
		}

		ret_formula = dbpatch_aggregate2formula(row[0], &request, &params, &params_alloc, &params_offset,
				&error);
		zbx_free_agent_request(&request);

		if (FAIL == ret_formula)
		{
			zabbix_log(LOG_LEVEL_WARNING, "Cannot convert aggregate checks item \"%s\": %s", row[0], error);
			zbx_free(error);
			continue;
		}

		esc = zbx_db_dyn_escape_field("items", "params", params);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
				"update items set type=15,params='%s' where itemid=%s;\n", esc, row[0]);
		zbx_free(esc);

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

	zbx_db_free_result(result);

	if (SUCCEED == ret)
	{
		if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
			ret = FAIL;
	}

	zbx_free(params);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030170(void)
{
#ifdef HAVE_MYSQL
	return DBcreate_index("items", "items_8", "key_(1024)", 0);
#else
	return DBcreate_index("items", "items_8", "key_", 0);
#endif
}

static int	DBpatch_5030171(void)
{
	/* When upgrading from version 5.0 or less the trigger_queue table will be created */
	/* later (DBpatch_5030172).                                                        */
	/* Only when upgrading from version 5.2 there will be existing trigger_queue table */
	/* to which primary key must be added. This is done by following steps:            */
	/*   1) rename existing table (DBpatch_5030171)                                    */
	/*   2) create new table with the primary key (DBpatch_5030172)                    */
	/*   2) copy data from old table into new table (DBpatch_5030173)                  */
	/*   2) delete the old (renamed) table (DBpatch_5030174)                           */
	if (SUCCEED != zbx_db_table_exists("trigger_queue"))
		return SUCCEED;

	return DBrename_table("trigger_queue", "trigger_queue_tmp");
}

static int	DBpatch_5030172(void)
{
	const zbx_db_table_t	table =
			{"trigger_queue", "trigger_queueid", 0,
				{
					{"trigger_queueid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"objectid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"type", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"clock", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"ns", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5030173(void)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	zbx_db_insert_t	db_insert;
	zbx_uint64_t	objectid, type, clock, ns;
	int		ret;

	if (SUCCEED != zbx_db_table_exists("trigger_queue_tmp"))
		return SUCCEED;

	zbx_db_insert_prepare(&db_insert, "trigger_queue", "trigger_queueid", "objectid", "type", "clock", "ns",
			(char *)NULL);

	result = zbx_db_select("select objectid,type,clock,ns from trigger_queue_tmp");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		ZBX_STR2UINT64(objectid, row[0]);
		ZBX_STR2UINT64(type, row[1]);
		ZBX_STR2UINT64(clock, row[2]);
		ZBX_STR2UINT64(ns, row[3]);

		zbx_db_insert_add_values(&db_insert, __UINT64_C(0), objectid, type, clock, ns);
	}
	zbx_db_free_result(result);

	zbx_db_insert_autoincrement(&db_insert, "trigger_queueid");
	ret = zbx_db_insert_execute(&db_insert);
	zbx_db_insert_clean(&db_insert);

	return ret;
}

static int	DBpatch_5030174(void)
{
	if (SUCCEED != zbx_db_table_exists("trigger_queue_tmp"))
		return SUCCEED;

	return DBdrop_table("trigger_queue_tmp");
}

static int	DBpatch_5030175(void)
{
	const zbx_db_field_t	field = {"type", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("valuemap_mapping", &field);
}

static int	DBpatch_5030176(void)
{
	return DBdrop_foreign_key("valuemap_mapping", 1);
}

static int	DBpatch_5030177(void)
{
	return DBdrop_index("valuemap_mapping", "valuemap_mapping_1");
}

static int	DBpatch_5030178(void)
{
	return DBcreate_index("valuemap_mapping", "valuemap_mapping_1", "valuemapid,value,type", 1);
}

static int	DBpatch_5030179(void)
{
	const zbx_db_field_t	field = {"valuemapid", NULL, "valuemap", "valuemapid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("valuemap_mapping", 1, &field);
}

static int	DBpatch_5030180(void)
{
	const zbx_db_field_t	field = {"sortorder", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("valuemap_mapping", &field);
}

static int	DBpatch_5030181(void)
{
	int		ret = SUCCEED;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select("select valuemapid from valuemap order by valuemapid asc");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		int		i = 0;
		char		*sql = NULL;
		zbx_uint64_t	valuemapid;
		size_t		sql_alloc = 0, sql_offset = 0;
		zbx_db_row_t	in_row;
		zbx_db_result_t	in_result;

		ZBX_DBROW2UINT64(valuemapid, row[0]);

		in_result = zbx_db_select("select valuemap_mappingid"
				" from valuemap_mapping"
				" where valuemapid=" ZBX_FS_UI64
				" order by valuemap_mappingid asc", valuemapid);

		while (NULL != (in_row = zbx_db_fetch(in_result)))
		{
			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
					"update valuemap_mapping set sortorder=%d where valuemap_mappingid=%s;\n",
					i, in_row[0]);
			i++;

			if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
				goto out;
		}

		if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
			ret = FAIL;
out:
		zbx_db_free_result(in_result);
		zbx_free(sql);

		if (FAIL == ret)
			break;
	}

	zbx_db_free_result(result);

	return ret;
}

#undef HOST_STATUS_TEMPLATE
#define HOST_STATUS_TEMPLATE		3
#undef ZBX_FLAG_DISCOVERY_NORMAL
#define ZBX_FLAG_DISCOVERY_NORMAL	0x00
#undef ZBX_FLAG_DISCOVERY_RULE
#define ZBX_FLAG_DISCOVERY_RULE		0x01
#undef ZBX_FLAG_DISCOVERY_PROTOTYPE
#define ZBX_FLAG_DISCOVERY_PROTOTYPE	0x02

#define ZBX_FIELD_UUID			{"uuid", "", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0}

static int	DBpatch_5030182(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("items", &field);
}

static int	DBpatch_5030183(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("hosts", &field);
}

static int	DBpatch_5030184(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("triggers", &field);
}

static int	DBpatch_5030185(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("dashboard", &field);
}

static int	DBpatch_5030186(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("graphs", &field);
}

static int	DBpatch_5030187(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("hstgrp", &field);
}

static int	DBpatch_5030188(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("httptest", &field);
}

static int	DBpatch_5030189(void)
{
	const zbx_db_field_t	field = ZBX_FIELD_UUID;

	return DBadd_field("valuemap", &field);
}

static char	*update_template_name(char *old)
{
	char	*ptr, new[MAX_STRING_LEN + 1], *ptr_snmp;

#define MIN_TEMPLATE_NAME_LEN	3

	ptr = old;

	if (NULL != zbx_regexp_match(old, "Template (APP|App|DB|Module|Net|OS|SAN|Server|Tel|VM) ", NULL) &&
			1 == sscanf(old, "Template %*[^ ] %" ZBX_STR(MAX_STRING_LEN) "[^\n]s", new) &&
			MIN_TEMPLATE_NAME_LEN <= strlen(new))
	{
		ptr = zbx_strdup(ptr, new);
	}

	ptr_snmp = zbx_string_replace(ptr, "SNMPv2", "SNMP");
	zbx_free(ptr);

	return ptr_snmp;
}

static char	*DBpatch_make_trigger_function(const char *name, const char *tpl, const char *key, const char *param)
{
	char	*template_name, *func = NULL;
	size_t	func_alloc = 0, func_offset = 0;

	template_name = zbx_strdup(NULL, tpl);
	template_name = update_template_name(template_name);

	zbx_snprintf_alloc(&func, &func_alloc, &func_offset, "%s(/%s/%s", name, template_name, key);

	if ('$' == *param && ',' == *++param)
		param++;

	if ('\0' != *param)
		zbx_snprintf_alloc(&func, &func_alloc, &func_offset, ",%s", param);

	zbx_chrcpy_alloc(&func, &func_alloc, &func_offset, ')');

	zbx_free(template_name);

	return func;
}

static int	DBpatch_5030190(void)
{
	int		ret = SUCCEED;
	char		*name, *uuid, *sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select h.hostid,h.host"
			" from hosts h"
			" where h.status=%d",
			HOST_STATUS_TEMPLATE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		name = zbx_strdup(NULL, row[1]);
		name = update_template_name(name);
		uuid = zbx_gen_uuid4(name);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update hosts set uuid='%s' where hostid=%s;\n",
				uuid, row[0]);
		zbx_free(name);
		zbx_free(uuid);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030191(void)
{
	int		ret = SUCCEED;
	char		*name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t		row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select i.itemid,i.key_,h.host"
			" from items i"
			" join hosts h on h.hostid=i.hostid"
			" where h.status=%d and i.flags in (%d,%d) and i.templateid is null",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		name = zbx_strdup(NULL, row[2]);
		name = update_template_name(name);
		seed = zbx_dsprintf(seed, "%s/%s", name, row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update items set uuid='%s' where itemid=%s;\n",
				uuid, row[0]);
		zbx_free(name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	compose_trigger_expression(zbx_db_row_t row, zbx_uint64_t rules, char **composed_expr)
{
	char			*trigger_expr;
	int			i;
	zbx_db_row_t		row2;
	zbx_db_result_t		result2;
	zbx_eval_context_t	ctx;

	for (i = 0; i < 2; i++)
	{
		int	j;
		char	*error = NULL;

		trigger_expr = row[i + 2];

		if ('\0' == *trigger_expr)
		{
			if (0 == i)
				zabbix_log(LOG_LEVEL_WARNING, "%s: empty expression for trigger %s", __func__, row[0]);

			continue;
		}

		if (FAIL == zbx_eval_parse_expression(&ctx, trigger_expr, rules | ZBX_EVAL_PARSE_STR_V64_COMPAT,
				&error))
		{
			zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression for %s: %s",
					__func__, row[0], error);
			zbx_free(error);
			return FAIL;
		}

		for (j = 0; j < ctx.stack.values_num; j++)
		{
			zbx_eval_token_t	*token = &ctx.stack.values[j];
			zbx_uint64_t		functionid;

			if (ZBX_EVAL_TOKEN_FUNCTIONID != token->type)
				continue;

			if (SUCCEED != zbx_is_uint64_n(ctx.expression + token->loc.l + 1,
					token->loc.r - token->loc.l - 1, &functionid))
			{
				zabbix_log(LOG_LEVEL_CRIT, "%s: error parsing trigger expression %s,"
						" zbx_is_uint64_n error", __func__, row[0]);
				return FAIL;
			}

			result2 = zbx_db_select(
					"select h.host,i.key_,f.name,f.parameter"
					" from functions f"
					" join items i on i.itemid=f.itemid"
					" join hosts h on h.hostid=i.hostid"
					" where f.functionid=" ZBX_FS_UI64,
					functionid);

			if (NULL != (row2 = zbx_db_fetch(result2)))
			{
				char	*func;

				func = DBpatch_make_trigger_function(row2[2], row2[0], row2[1], row2[3]);
				zbx_variant_clear(&token->value);
				zbx_variant_set_str(&token->value, func);
			}

			zbx_db_free_result(result2);
		}

		zbx_eval_compose_expression(&ctx, &composed_expr[i]);
		zbx_eval_clear(&ctx);
	}

	return SUCCEED;
}

static int	DBpatch_5030192(void)
{
	int		ret = SUCCEED;
	char		*sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select distinct t.triggerid,t.description,t.expression,t.recovery_expression"
			" from triggers t"
			" join functions f on f.triggerid=t.triggerid"
			" join items i on i.itemid=f.itemid"
			" join hosts h on h.hostid=i.hostid and h.status=%d"
			" where t.templateid is null and t.flags=%d",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_NORMAL);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char		*uuid, *seed = NULL;
		char		*composed_expr[] = { NULL, NULL };
		size_t		seed_alloc = 0, seed_offset = 0;

		if (FAIL == compose_trigger_expression(row, ZBX_EVAL_PARSE_TRIGGER_EXPRESSION, composed_expr))
		{
			ret = FAIL;
			goto out;
		}

		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s/", row[1]);
		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", composed_expr[0]);
		if (NULL != composed_expr[1])
			zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", composed_expr[1]);

		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set uuid='%s'"
				" where triggerid=%s;\n", uuid, row[0]);

		zbx_free(composed_expr[0]);
		zbx_free(composed_expr[1]);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030193(void)
{
	int		ret = SUCCEED;
	char		*host_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0, seed_alloc = 0, seed_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select distinct g.graphid,g.name"
			" from graphs g"
			" join graphs_items gi on gi.graphid=g.graphid"
			" join items i on i.itemid=gi.itemid"
			" join hosts h on h.hostid=i.hostid and h.status=%d"
			" where g.templateid is null and g.flags=%d",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_NORMAL);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		zbx_db_row_t	row2;
		zbx_db_result_t	result2;

		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", row[1]);

		result2 = zbx_db_select(
				"select h.host"
				" from graphs_items gi"
				" join items i on i.itemid=gi.itemid"
				" join hosts h on h.hostid=i.hostid"
				" where gi.graphid=%s"
				" order by h.host",
				row[0]);

		while (NULL != (row2 = zbx_db_fetch(result2)))
		{
			host_name = zbx_strdup(NULL, row2[0]);
			host_name = update_template_name(host_name);

			zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", host_name);
			zbx_free(host_name);
		}

		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update graphs set uuid='%s'"
				" where graphid=%s;\n", uuid, row[0]);
		zbx_free(uuid);
		zbx_free(seed);

		zbx_db_free_result(result2);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030194(void)
{
	int		ret = SUCCEED;
	char		*template_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select d.dashboardid,d.name,h.host"
			" from dashboard d"
			" join hosts h on h.hostid=d.templateid"
			" where h.status=%d",
			HOST_STATUS_TEMPLATE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		template_name = zbx_strdup(NULL, row[2]);
		template_name = update_template_name(template_name);
		seed = zbx_dsprintf(seed, "%s/%s", template_name, row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
				"update dashboard set uuid='%s' where dashboardid=%s;\n", uuid, row[0]);
		zbx_free(template_name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030195(void)
{
	int		ret = SUCCEED;
	char		*template_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select ht.httptestid,ht.name,h.host"
			" from httptest ht"
			" join hosts h on h.hostid=ht.hostid and h.status=%d"
			" where ht.templateid is null",
			HOST_STATUS_TEMPLATE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		template_name = zbx_strdup(NULL, row[2]);
		template_name = update_template_name(template_name);
		seed = zbx_dsprintf(seed, "%s/%s", template_name, row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
				"update httptest set uuid='%s' where httptestid=%s;\n", uuid, row[0]);
		zbx_free(template_name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030196(void)
{
	int		ret = SUCCEED;
	char		*template_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select v.valuemapid,v.name,h.host"
			" from valuemap v"
			" join hosts h on h.hostid=v.hostid"
			" where h.status=%d",
			HOST_STATUS_TEMPLATE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		template_name = zbx_strdup(NULL, row[2]);
		template_name = update_template_name(template_name);
		seed = zbx_dsprintf(seed, "%s/%s", template_name, row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
				"update valuemap set uuid='%s' where valuemapid=%s;\n", uuid, row[0]);
		zbx_free(template_name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030197(void)
{
	int		ret = SUCCEED;
	char		*uuid, *sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select("select groupid,name from hstgrp");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		uuid = zbx_gen_uuid4(row[1]);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
				"update hstgrp set uuid='%s' where groupid=%s;\n", uuid, row[0]);
		zbx_free(uuid);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030198(void)
{
	int		ret = SUCCEED;
	char		*template_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select i.itemid,i.key_,h.host,i2.key_"
			" from items i"
			" join hosts h on h.hostid=i.hostid"
			" join item_discovery id on id.itemid=i.itemid"
			" join items i2 on id.parent_itemid=i2.itemid"
			" where h.status=%d and i.flags=%d and i.templateid is null",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_PROTOTYPE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		template_name = zbx_strdup(NULL, row[2]);
		template_name = update_template_name(template_name);
		seed = zbx_dsprintf(seed, "%s/%s/%s", template_name, row[3], row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update items set uuid='%s' where itemid=%s;\n",
				uuid, row[0]);
		zbx_free(template_name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030199(void)
{
	int			ret = SUCCEED;
	char			*sql = NULL;
	size_t			sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t		row;
	zbx_db_result_t		result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select distinct t.triggerid,t.description,t.expression,t.recovery_expression"
			" from triggers t"
			" join functions f on f.triggerid=t.triggerid"
			" join items i on i.itemid=f.itemid"
			" join hosts h on h.hostid=i.hostid and h.status=%d"
			" where t.templateid is null and t.flags=%d",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_PROTOTYPE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		char		*uuid, *seed = NULL;
		char		*composed_expr[] = { NULL, NULL };
		size_t		seed_alloc = 0, seed_offset = 0;
		zbx_db_row_t	row2;
		zbx_db_result_t	result2;

		result2 = zbx_db_select(
				"select distinct i2.key_"
				" from items i"
				" join item_discovery id on id.itemid=i.itemid"
				" join items i2 on id.parent_itemid=i2.itemid"
				" join functions f on i.itemid=f.itemid and f.triggerid=%s"
				" where i.flags=%d",
				row[0], ZBX_FLAG_DISCOVERY_PROTOTYPE);

		if (NULL == (row2 = zbx_db_fetch(result2)))
		{
			zbx_db_free_result(result2);
			continue;
		}

		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", row2[0]);

		zbx_db_free_result(result2);

		if (FAIL == compose_trigger_expression(row, ZBX_EVAL_TRIGGER_EXPRESSION_LLD, composed_expr))
		{
			ret = FAIL;
			goto out;
		}

		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s/", row[1]);
		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s", composed_expr[0]);
		if (NULL != composed_expr[1])
			zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "/%s", composed_expr[1]);

		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set uuid='%s'"
				" where triggerid=%s;\n", uuid, row[0]);

		zbx_free(composed_expr[0]);
		zbx_free(composed_expr[1]);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030200(void)
{
	int		ret = SUCCEED;
	char		*templ_name, *uuid, *sql = NULL, *seed = NULL;
	size_t		sql_alloc = 0, sql_offset = 0, seed_alloc = 0, seed_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select distinct g.graphid,g.name,h.host,i2.key_"
			" from graphs g"
			" join graphs_items gi on gi.graphid=g.graphid"
			" join items i on i.itemid=gi.itemid and i.flags=%d"
			" join hosts h on h.hostid=i.hostid and h.status=%d"
			" join item_discovery id on id.itemid=i.itemid"
			" join items i2 on id.parent_itemid=i2.itemid"
			" where g.templateid is null and g.flags=%d",
			ZBX_FLAG_DISCOVERY_PROTOTYPE, HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_PROTOTYPE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		templ_name = zbx_strdup(NULL, row[2]);
		templ_name = update_template_name(templ_name);
		zbx_snprintf_alloc(&seed, &seed_alloc, &seed_offset, "%s/%s/%s", templ_name, row[3], row[1]);

		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update graphs set uuid='%s'"
				" where graphid=%s;\n", uuid, row[0]);
		zbx_free(templ_name);
		zbx_free(uuid);
		zbx_free(seed);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

static int	DBpatch_5030201(void)
{
	int		ret = SUCCEED;
	char		*name_tmpl, *uuid, *seed = NULL, *sql = NULL;
	size_t		sql_alloc = 0, sql_offset = 0;
	zbx_db_row_t	row;
	zbx_db_result_t	result;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select(
			"select h.hostid,h.host,h2.host,i.key_"
			" from hosts h"
			" join host_discovery hd on hd.hostid=h.hostid"
			" join items i on i.itemid=hd.parent_itemid"
			" join hosts h2 on h2.hostid=i.hostid and h2.status=%d"
			" where h.flags=%d and h.templateid is null",
			HOST_STATUS_TEMPLATE, ZBX_FLAG_DISCOVERY_PROTOTYPE);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		name_tmpl = zbx_strdup(NULL, row[2]);
		name_tmpl = update_template_name(name_tmpl);
		seed = zbx_dsprintf(seed, "%s/%s/%s", name_tmpl, row[3], row[1]);
		uuid = zbx_gen_uuid4(seed);
		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update hosts set uuid='%s' where hostid=%s;\n",
				uuid, row[0]);
		zbx_free(name_tmpl);
		zbx_free(seed);
		zbx_free(uuid);

		if (SUCCEED != (ret = zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
			goto out;
	}

	if (ZBX_DB_OK > zbx_db_flush_overflowed_sql(sql, sql_offset))
		ret = FAIL;
out:
	zbx_db_free_result(result);
	zbx_free(sql);

	return ret;
}

#undef HOST_STATUS_TEMPLATE
#undef ZBX_FLAG_DISCOVERY_NORMAL
#undef ZBX_FLAG_DISCOVERY_RULE
#undef ZBX_FLAG_DISCOVERY_PROTOTYPE

#endif

DBPATCH_START(5030)

/* version, duplicates flag, mandatory flag */

DBPATCH_ADD(5030000, 0, 1)
DBPATCH_ADD(5030001, 0, 1)
DBPATCH_ADD(5030002, 0, 1)
DBPATCH_ADD(5030003, 0, 1)
DBPATCH_ADD(5030004, 0, 1)
DBPATCH_ADD(5030005, 0, 1)
DBPATCH_ADD(5030006, 0, 1)
DBPATCH_ADD(5030007, 0, 1)
DBPATCH_ADD(5030008, 0, 1)
DBPATCH_ADD(5030009, 0, 1)
DBPATCH_ADD(5030010, 0, 1)
DBPATCH_ADD(5030011, 0, 1)
DBPATCH_ADD(5030012, 0, 1)
DBPATCH_ADD(5030013, 0, 1)
DBPATCH_ADD(5030014, 0, 1)
DBPATCH_ADD(5030015, 0, 1)
DBPATCH_ADD(5030016, 0, 1)
DBPATCH_ADD(5030017, 0, 1)
DBPATCH_ADD(5030018, 0, 1)
DBPATCH_ADD(5030019, 0, 1)
DBPATCH_ADD(5030020, 0, 1)
DBPATCH_ADD(5030021, 0, 1)
DBPATCH_ADD(5030022, 0, 1)
DBPATCH_ADD(5030023, 0, 1)
DBPATCH_ADD(5030024, 0, 1)
DBPATCH_ADD(5030025, 0, 1)
DBPATCH_ADD(5030026, 0, 1)
DBPATCH_ADD(5030027, 0, 1)
DBPATCH_ADD(5030028, 0, 1)
DBPATCH_ADD(5030029, 0, 1)
DBPATCH_ADD(5030030, 0, 1)
DBPATCH_ADD(5030031, 0, 1)
DBPATCH_ADD(5030032, 0, 1)
DBPATCH_ADD(5030033, 0, 1)
DBPATCH_ADD(5030034, 0, 1)
DBPATCH_ADD(5030035, 0, 1)
DBPATCH_ADD(5030036, 0, 1)
DBPATCH_ADD(5030037, 0, 1)
DBPATCH_ADD(5030038, 0, 1)
DBPATCH_ADD(5030039, 0, 1)
DBPATCH_ADD(5030040, 0, 1)
DBPATCH_ADD(5030041, 0, 1)
DBPATCH_ADD(5030042, 0, 1)
DBPATCH_ADD(5030043, 0, 1)
DBPATCH_ADD(5030044, 0, 1)
DBPATCH_ADD(5030045, 0, 1)
DBPATCH_ADD(5030046, 0, 1)
DBPATCH_ADD(5030047, 0, 1)
DBPATCH_ADD(5030048, 0, 1)
DBPATCH_ADD(5030049, 0, 1)
DBPATCH_ADD(5030050, 0, 1)
DBPATCH_ADD(5030051, 0, 1)
DBPATCH_ADD(5030052, 0, 1)
DBPATCH_ADD(5030053, 0, 1)
DBPATCH_ADD(5030054, 0, 1)
DBPATCH_ADD(5030055, 0, 1)
DBPATCH_ADD(5030056, 0, 1)
DBPATCH_ADD(5030057, 0, 1)
DBPATCH_ADD(5030058, 0, 1)
DBPATCH_ADD(5030059, 0, 1)
DBPATCH_ADD(5030060, 0, 1)
DBPATCH_ADD(5030061, 0, 1)
DBPATCH_ADD(5030062, 0, 1)
DBPATCH_ADD(5030063, 0, 1)
DBPATCH_ADD(5030064, 0, 1)
DBPATCH_ADD(5030065, 0, 1)
DBPATCH_ADD(5030066, 0, 1)
DBPATCH_ADD(5030067, 0, 1)
DBPATCH_ADD(5030068, 0, 1)
DBPATCH_ADD(5030069, 0, 1)
DBPATCH_ADD(5030070, 0, 1)
DBPATCH_ADD(5030071, 0, 1)
DBPATCH_ADD(5030072, 0, 1)
DBPATCH_ADD(5030073, 0, 1)
DBPATCH_ADD(5030074, 0, 1)
DBPATCH_ADD(5030075, 0, 1)
DBPATCH_ADD(5030076, 0, 1)
DBPATCH_ADD(5030077, 0, 1)
DBPATCH_ADD(5030078, 0, 1)
DBPATCH_ADD(5030079, 0, 1)
DBPATCH_ADD(5030080, 0, 1)
DBPATCH_ADD(5030081, 0, 1)
DBPATCH_ADD(5030082, 0, 1)
DBPATCH_ADD(5030083, 0, 1)
DBPATCH_ADD(5030084, 0, 1)
DBPATCH_ADD(5030085, 0, 1)
DBPATCH_ADD(5030086, 0, 1)
DBPATCH_ADD(5030087, 0, 1)
DBPATCH_ADD(5030088, 0, 1)
DBPATCH_ADD(5030089, 0, 1)
DBPATCH_ADD(5030090, 0, 1)
DBPATCH_ADD(5030091, 0, 1)
DBPATCH_ADD(5030092, 0, 1)
DBPATCH_ADD(5030093, 0, 1)
DBPATCH_ADD(5030094, 0, 1)
DBPATCH_ADD(5030095, 0, 1)
DBPATCH_ADD(5030096, 0, 1)
DBPATCH_ADD(5030097, 0, 1)
DBPATCH_ADD(5030098, 0, 1)
DBPATCH_ADD(5030099, 0, 1)
DBPATCH_ADD(5030100, 0, 1)
DBPATCH_ADD(5030101, 0, 1)
DBPATCH_ADD(5030102, 0, 1)
DBPATCH_ADD(5030103, 0, 1)
DBPATCH_ADD(5030104, 0, 1)
DBPATCH_ADD(5030105, 0, 1)
DBPATCH_ADD(5030106, 0, 1)
DBPATCH_ADD(5030107, 0, 1)
DBPATCH_ADD(5030108, 0, 1)
DBPATCH_ADD(5030109, 0, 1)
DBPATCH_ADD(5030110, 0, 1)
DBPATCH_ADD(5030111, 0, 1)
DBPATCH_ADD(5030112, 0, 1)
DBPATCH_ADD(5030113, 0, 1)
DBPATCH_ADD(5030114, 0, 1)
DBPATCH_ADD(5030115, 0, 1)
DBPATCH_ADD(5030116, 0, 1)
DBPATCH_ADD(5030117, 0, 1)
DBPATCH_ADD(5030118, 0, 1)
DBPATCH_ADD(5030119, 0, 1)
DBPATCH_ADD(5030120, 0, 1)
DBPATCH_ADD(5030121, 0, 1)
DBPATCH_ADD(5030122, 0, 1)
DBPATCH_ADD(5030123, 0, 1)
DBPATCH_ADD(5030127, 0, 1)
DBPATCH_ADD(5030128, 0, 1)
DBPATCH_ADD(5030129, 0, 1)
DBPATCH_ADD(5030130, 0, 1)
DBPATCH_ADD(5030131, 0, 1)
DBPATCH_ADD(5030132, 0, 1)
DBPATCH_ADD(5030133, 0, 1)
DBPATCH_ADD(5030134, 0, 1)
DBPATCH_ADD(5030135, 0, 1)
DBPATCH_ADD(5030136, 0, 1)
DBPATCH_ADD(5030137, 0, 1)
DBPATCH_ADD(5030138, 0, 1)
DBPATCH_ADD(5030139, 0, 1)
DBPATCH_ADD(5030140, 0, 1)
DBPATCH_ADD(5030141, 0, 1)
DBPATCH_ADD(5030142, 0, 1)
DBPATCH_ADD(5030143, 0, 1)
DBPATCH_ADD(5030144, 0, 1)
DBPATCH_ADD(5030145, 0, 1)
DBPATCH_ADD(5030146, 0, 1)
DBPATCH_ADD(5030147, 0, 1)
DBPATCH_ADD(5030148, 0, 1)
DBPATCH_ADD(5030149, 0, 1)
DBPATCH_ADD(5030150, 0, 1)
DBPATCH_ADD(5030151, 0, 1)
DBPATCH_ADD(5030152, 0, 1)
DBPATCH_ADD(5030153, 0, 1)
DBPATCH_ADD(5030154, 0, 1)
DBPATCH_ADD(5030155, 0, 1)
DBPATCH_ADD(5030156, 0, 1)
DBPATCH_ADD(5030157, 0, 1)
DBPATCH_ADD(5030158, 0, 1)
DBPATCH_ADD(5030159, 0, 1)
DBPATCH_ADD(5030160, 0, 1)
DBPATCH_ADD(5030161, 0, 1)
DBPATCH_ADD(5030162, 0, 1)
DBPATCH_ADD(5030163, 0, 1)
DBPATCH_ADD(5030164, 0, 1)
DBPATCH_ADD(5030165, 0, 1)
DBPATCH_ADD(5030166, 0, 1)
DBPATCH_ADD(5030167, 0, 1)
DBPATCH_ADD(5030168, 0, 1)
DBPATCH_ADD(5030169, 0, 1)
DBPATCH_ADD(5030170, 0, 1)
DBPATCH_ADD(5030171, 0, 1)
DBPATCH_ADD(5030172, 0, 1)
DBPATCH_ADD(5030173, 0, 1)
DBPATCH_ADD(5030174, 0, 1)
DBPATCH_ADD(5030175, 0, 1)
DBPATCH_ADD(5030176, 0, 1)
DBPATCH_ADD(5030177, 0, 1)
DBPATCH_ADD(5030178, 0, 1)
DBPATCH_ADD(5030179, 0, 1)
DBPATCH_ADD(5030180, 0, 1)
DBPATCH_ADD(5030181, 0, 1)
DBPATCH_ADD(5030182, 0, 1)
DBPATCH_ADD(5030183, 0, 1)
DBPATCH_ADD(5030184, 0, 1)
DBPATCH_ADD(5030185, 0, 1)
DBPATCH_ADD(5030186, 0, 1)
DBPATCH_ADD(5030187, 0, 1)
DBPATCH_ADD(5030188, 0, 1)
DBPATCH_ADD(5030189, 0, 1)
DBPATCH_ADD(5030190, 0, 1)
DBPATCH_ADD(5030191, 0, 1)
DBPATCH_ADD(5030192, 0, 1)
DBPATCH_ADD(5030193, 0, 1)
DBPATCH_ADD(5030194, 0, 1)
DBPATCH_ADD(5030195, 0, 1)
DBPATCH_ADD(5030196, 0, 1)
DBPATCH_ADD(5030197, 0, 1)
DBPATCH_ADD(5030198, 0, 1)
DBPATCH_ADD(5030199, 0, 1)
DBPATCH_ADD(5030200, 0, 1)
DBPATCH_ADD(5030201, 0, 1)

DBPATCH_END()