/*
** 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/>.
**/

#ifndef ZABBIX_ZBXDB_H
#define ZABBIX_ZBXDB_H

#include "zbxcommon.h"
#include "zbxjson.h"
#include "zbxdbschema.h"

#define ZBX_DB_OK	0
#define ZBX_DB_FAIL	-1
#define ZBX_DB_DOWN	-2

#define ZBX_DB_TLS_CONNECT_REQUIRED_TXT		"required"
#define ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT	"verify_ca"
#define ZBX_DB_TLS_CONNECT_VERIFY_FULL_TXT	"verify_full"

typedef char	**zbx_db_row_t;
typedef struct zbx_db_result	*zbx_db_result_t;

typedef struct zbx_dbconn zbx_dbconn_t;

/* database field value */
typedef union
{
	int		i32;
	zbx_uint64_t	ui64;
	double		dbl;
	char		*str;
}
zbx_db_value_t;

typedef struct
{
	char		*dbhost;
	char		*dbname;
	char		*dbschema;
	char		*dbuser;
	char		*dbpassword;
	char		*dbsocket;
	char		*db_tls_connect;
	char		*db_tls_cert_file;
	char		*db_tls_key_file;
	char		*db_tls_ca_file;
	char		*db_tls_cipher;
	char		*db_tls_cipher_13;
	unsigned int	dbport;
	int		log_slow_queries;
	int		read_only_recoverable;
}
zbx_db_config_t;

#ifdef HAVE_SQLITE3
	/* we have to put double % here for sprintf */
#	define ZBX_SQL_MOD(x, y) #x "%%" #y
#else
#	define ZBX_SQL_MOD(x, y) "mod(" #x "," #y ")"
#endif

#ifdef HAVE_SQLITE3
#	define ZBX_FOR_UPDATE	""	/* SQLite3 does not support "select ... for update" */
#else
#	define ZBX_FOR_UPDATE	" for update"
#endif

int	zbx_db_init(char **error);
void	zbx_db_deinit(void);


typedef enum
{
	ERR_Z3001 = 3001,
	ERR_Z3002,
	ERR_Z3003,
	ERR_Z3004,
	ERR_Z3005,
	ERR_Z3006,
	ERR_Z3007,
	ERR_Z3008,
	ERR_Z3009
}
zbx_err_codes_t;

#ifdef HAVE_POSTGRESQL
int	zbx_tsdb_get_version(void);
#endif

#if defined (HAVE_MYSQL)
void	zbx_mysql_escape_bin(const char *src, char *dst, size_t size);
#elif defined(HAVE_POSTGRESQL)
void	zbx_postgresql_escape_bin(const char *src, char **dst, size_t size);
#endif

typedef enum
{
	ESCAPE_SEQUENCE_OFF,
	ESCAPE_SEQUENCE_ON
}
zbx_escape_sequence_t;

#define ZBX_SQL_LIKE_ESCAPE_CHAR '!'
char		*zbx_db_dyn_escape_like_pattern(const char *src);

size_t		zbx_db_strlen_n(const char *text_loc, size_t maxlen);

#define ZBX_DB_EXTENSION_TIMESCALEDB	"timescaledb"

#if defined(HAVE_POSTGRESQL)
#	define ZBX_SUPPORTED_DB_CHARACTER_SET	"utf8"
#elif defined(HAVE_MYSQL)
#	define ZBX_DB_STRLIST_DELIM		','
#	define ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8	"utf8"
#	define ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8MB3	"utf8mb3"
#	define ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8MB4 	"utf8mb4"
#	define ZBX_SUPPORTED_DB_CHARACTER_SET		ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8 ","\
							ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8MB3 ","\
							ZBX_SUPPORTED_DB_CHARACTER_SET_UTF8MB4
#	define ZBX_SUPPORTED_DB_COLLATION		"utf8_bin,utf8mb3_bin,utf8mb4_bin"
#endif

typedef enum
{	/* db version status flags shared with FRONTEND */
	DB_VERSION_SUPPORTED,
	DB_VERSION_LOWER_THAN_MINIMUM,
	DB_VERSION_HIGHER_THAN_MAXIMUM,
	DB_VERSION_FAILED_TO_RETRIEVE,
	DB_VERSION_NOT_SUPPORTED_ERROR,
	DB_VERSION_NOT_SUPPORTED_WARNING,
	DB_VERSION_HIGHER_THAN_MAXIMUM_ERROR,
	DB_VERSION_HIGHER_THAN_MAXIMUM_WARNING
}
zbx_db_version_status_t;

typedef enum
{	/* db extension error codes shared with FRONTEND */
	ZBX_EXT_ERR_UNDEFINED = 0,
	ZBX_EXT_SUCCEED = 1,
	/* ZBX_TIMESCALEDB_POSTGRES_TOO_OLD, obsoleted since Zabbix 7.0 */
	ZBX_TIMESCALEDB_VERSION_FAILED_TO_RETRIEVE = 3,
	ZBX_TIMESCALEDB_VERSION_LOWER_THAN_MINIMUM,
	ZBX_TIMESCALEDB_VERSION_NOT_SUPPORTED,
	ZBX_TIMESCALEDB_VERSION_HIGHER_THAN_MAXIMUM,
	ZBX_TIMESCALEDB_LICENSE_NOT_COMMUNITY
}
zbx_db_ext_err_code_t;

struct zbx_db_version_info_t
{
	/* information about database server */

	const char		*database;

	zbx_uint32_t		current_version;
	zbx_uint32_t		min_version;
	zbx_uint32_t		max_version;
	zbx_uint32_t		min_supported_version;

	char			*friendly_current_version;
	const char		*friendly_min_version;
	const char		*friendly_max_version;
	const char		*friendly_min_supported_version;

	zbx_db_version_status_t	flag;

	int			history_pk;

	/* information about database server extension */

	char			*extension;

	zbx_uint32_t		ext_current_version;
	zbx_uint32_t		ext_min_version;
	zbx_uint32_t		ext_max_version;
	zbx_uint32_t		ext_min_supported_version;

	char			*ext_friendly_current_version;
	const char		*ext_friendly_min_version;
	const char		*ext_friendly_max_version;
	const char		*ext_friendly_min_supported_version;

	zbx_db_version_status_t	ext_flag;

	zbx_db_ext_err_code_t	ext_err_code;

	int			history_compressed_chunks;
	int			trends_compressed_chunks;
};

#ifdef HAVE_POSTGRESQL
void	zbx_tsdb_info_extract(struct zbx_db_version_info_t *version_info);
void	zbx_tsdb_set_compression_availability(int compression_availabile);
int	zbx_tsdb_get_compression_availability(void);
void	zbx_tsdb_extract_compressed_chunk_flags(struct zbx_db_version_info_t *version_info);
#endif

int	zbx_db_version_check(const char *database, zbx_uint32_t current_version, zbx_uint32_t min_version,
		zbx_uint32_t max_version, zbx_uint32_t min_supported_version);
void	zbx_db_version_json_create(struct zbx_json *json, struct zbx_db_version_info_t *info);

#if defined(HAVE_MYSQL)
#	define ZBX_DB_TIMESTAMP()	"unix_timestamp()"
#	define ZBX_DB_CHAR_LENGTH(str)	"char_length(" #str ")"
#elif defined(HAVE_POSTGRESQL)
#	define ZBX_DB_TIMESTAMP()	"cast(extract(epoch from now()) as int)"
#	define ZBX_DB_CHAR_LENGTH(str)	"char_length(" #str ")"
#else
#	define ZBX_DB_TIMESTAMP()	"cast(strftime('%s', 'now') as integer)"
#	define ZBX_DB_CHAR_LENGTH(str)	"length(" #str ")"
#endif

#define ZBX_DB_CONNECT_NORMAL	0
#define ZBX_DB_CONNECT_EXIT	1
#define ZBX_DB_CONNECT_ONCE	2

ZBX_PTR_VECTOR_DECL(const_db_field_ptr, const zbx_db_field_t *)
ZBX_PTR_VECTOR_DECL(db_value_ptr, zbx_db_value_t *)

typedef struct
{
	/* database connection */
	zbx_dbconn_t			*db;
	/* the target table */
	const zbx_db_table_t		*table;
	/* the fields to insert (pointers to the zbx_db_field_t structures from database schema) */
	zbx_vector_const_db_field_ptr_t	fields;
	/* the values rows to insert (pointers to arrays of zbx_db_value_t structures) */
	zbx_vector_db_value_ptr_t	rows;
	/* index of autoincrement field */
	int				autoincrement;
	/* the last id assigned by autoincrement */
	zbx_uint64_t			lastid;
}
zbx_db_insert_t;

void	zbx_init_library_db(zbx_db_config_t *config);
void	zbx_deinit_library_db(zbx_db_config_t *config);

zbx_dbconn_t	*zbx_dbconn_create(void);
void	zbx_dbconn_free(zbx_dbconn_t *db);

int	zbx_dbconn_set_connect_options(zbx_dbconn_t *db, int options);
void	zbx_dbconn_set_autoincrement(zbx_dbconn_t *db, int options);

int	zbx_dbconn_open(zbx_dbconn_t *db);
void 	zbx_dbconn_close(zbx_dbconn_t *db);

int	zbx_dbconn_execute(zbx_dbconn_t *db, const char *fmt, ...);
int	zbx_dbconn_vexecute(zbx_dbconn_t *db, const char *fmt, va_list args);

zbx_db_result_t	zbx_dbconn_vselect(zbx_dbconn_t *db, const char *fmt, va_list args);
zbx_db_result_t	zbx_dbconn_select(zbx_dbconn_t *db, const char *fmt, ...);
zbx_db_result_t	zbx_dbconn_select_n(zbx_dbconn_t *db, const char *query, int n);

zbx_db_row_t	zbx_db_fetch(zbx_db_result_t result);
void	zbx_db_free_result(zbx_db_result_t result);

int	zbx_dbconn_begin(zbx_dbconn_t *db);
int	zbx_dbconn_commit(zbx_dbconn_t *db);
int	zbx_dbconn_rollback(zbx_dbconn_t *db);
int	zbx_dbconn_end(zbx_dbconn_t *db, int ret);

zbx_uint64_t	zbx_dbconn_get_maxid_num(zbx_dbconn_t *db, const char *tablename, int num);

/* bulk insert support */
void	zbx_dbconn_prepare_insert_dyn(zbx_dbconn_t *db, zbx_db_insert_t *db_insert, const zbx_db_table_t *table,
		const zbx_db_field_t * const *fields, int fields_num);
void	zbx_dbconn_prepare_vinsert(zbx_dbconn_t *db, zbx_db_insert_t *db_insert, const char *table, va_list args);
void	zbx_dbconn_prepare_insert(zbx_dbconn_t *db, zbx_db_insert_t *db_insert, const char *table, ...);
void	zbx_db_insert_add_values(zbx_db_insert_t *db_insert, ...);
void	zbx_db_insert_add_values_dyn(zbx_db_insert_t *db_insert, zbx_db_value_t **values, int values_num);
int	zbx_db_insert_execute(zbx_db_insert_t *db_insert);
void	zbx_db_insert_autoincrement(zbx_db_insert_t *db_insert, const char *field_name);
zbx_uint64_t	zbx_db_insert_get_lastid(zbx_db_insert_t *self);
void	zbx_db_insert_clean(zbx_db_insert_t *db_insert);

void	zbx_dbconn_extract_version_info(zbx_dbconn_t *db, struct zbx_db_version_info_t *version_info);

const char	*zbx_dbconn_last_strerr(zbx_dbconn_t *db);
zbx_err_codes_t	zbx_dbconn_last_errcode(zbx_dbconn_t *db);

zbx_db_config_t	*zbx_db_config_create(void);
void	zbx_db_config_free(zbx_db_config_t *config);

int	zbx_dbconn_lock_record(zbx_dbconn_t *db, const char *table, zbx_uint64_t id, const char *add_field,
		zbx_uint64_t add_id);
int	zbx_dbconn_lock_records(zbx_dbconn_t *db, const char *table, const zbx_vector_uint64_t *ids);
int	zbx_dbconn_lock_ids(zbx_dbconn_t *db, const char *table_name, const char *field_name, zbx_vector_uint64_t *ids);

int	zbx_db_config_validate_features(zbx_db_config_t *config, unsigned char program_type);
void	zbx_db_config_validate(zbx_db_config_t *config);

int	zbx_dbconn_check_extension(zbx_dbconn_t *db, struct zbx_db_version_info_t *info, int allow_unsupported);

#ifdef HAVE_POSTGRESQL
void	zbx_dbconn_tsdb_extract_compressed_chunk_flags(zbx_dbconn_t *db, struct zbx_db_version_info_t *version_info);
void	zbx_dbconn_tsdb_info_extract(zbx_dbconn_t *db, struct zbx_db_version_info_t *version_info);
int	zbx_dbconn_tsdb_get_version(zbx_dbconn_t *db);
void	zbx_dbconn_tsdb_set_compression_availability(zbx_dbconn_t *db, int compression_availabile);
int	zbx_dbconn_tsdb_get_compression_availability(zbx_dbconn_t *db);
#endif

char	*zbx_db_dyn_escape_field(const char *table_name, const char *field_name, const char *src);
char	*zbx_db_dyn_escape_string(const char *src);
char	*zbx_db_dyn_escape_string_len(const char *src, size_t length);
char	*zbx_db_dyn_escape_like_pattern(const char *src);

void	zbx_db_add_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
		const zbx_uint64_t *values, const int num);
void	zbx_db_add_str_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
		const char * const *values, const int num);

const zbx_db_table_t	*zbx_db_get_table(const char *tablename);
const zbx_db_field_t	*zbx_db_get_field(const zbx_db_table_t *table, const char *fieldname);

int		zbx_db_validate_field_size(const char *tablename, const char *fieldname, const char *str);

#define zbx_db_get_maxid(table)	zbx_db_get_maxid_num(table, 1)
zbx_uint64_t	zbx_db_get_maxid_num(const char *tablename, int num);

int	zbx_db_get_row_num(zbx_db_result_t result);

int	zbx_db_is_null(const char *field);

#if defined(HAVE_POSTGRESQL)
char	*zbx_db_get_schema_esc(void);
#endif

int	zbx_dbconn_execute_overflowed_sql(zbx_dbconn_t *db, char **sql, size_t *sql_alloc, size_t *sql_offset);
int	zbx_dbconn_flush_overflowed_sql(zbx_dbconn_t *db, char *sql, size_t sql_offset);

/* compatibility wrappers */
void	zbx_db_init_autoincrement_options(void);
int	zbx_db_connect(int flag);
void	zbx_db_close(void);
void	zbx_db_begin(void);
int	zbx_db_commit(void);
void	zbx_db_rollback(void);
int	zbx_db_end(int ret);
int	zbx_db_execute(const char *fmt, ...);
int	zbx_db_execute_once(const char *fmt, ...);
zbx_db_result_t	zbx_db_select(const char *fmt, ...);
zbx_db_result_t	zbx_db_select_n(const char *query, int n);
zbx_uint64_t	zbx_db_get_maxid_num(const char *tablename, int num);
void	zbx_db_insert_prepare_dyn(zbx_db_insert_t *db_insert, const zbx_db_table_t *table, const zbx_db_field_t **fields,
		int fields_num);
void	zbx_db_insert_prepare(zbx_db_insert_t *self, const char *table, ...);

void	zbx_db_extract_version_info(struct zbx_db_version_info_t *version_info);
void	zbx_tsdb_extract_compressed_chunk_flags(struct zbx_db_version_info_t *version_info);
void	zbx_tsdb_info_extract(struct zbx_db_version_info_t *version_info);
int	zbx_tsdb_get_version(void);
void	zbx_tsdb_set_compression_availability(int compression_availabile);
int	zbx_tsdb_get_compression_availability(void);
const char	*zbx_db_last_strerr(void);
zbx_err_codes_t	zbx_db_last_errcode(void);
int	zbx_db_lock_record(const char *table, zbx_uint64_t id, const char *add_field, zbx_uint64_t add_id);
int	zbx_db_lock_records(const char *table, const zbx_vector_uint64_t *ids);
int	zbx_db_lock_ids(const char *table_name, const char *field_name, zbx_vector_uint64_t *ids);
int	zbx_db_check_extension(struct zbx_db_version_info_t *info, int allow_unsupported);
int	zbx_db_flush_overflowed_sql(char *sql, size_t sql_offset);
int	zbx_db_execute_overflowed_sql(char **sql, size_t *sql_alloc, size_t *sql_offset);

#endif