1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21
22 #include "zbxdb.h"
23
24 #if defined(HAVE_MYSQL)
25 # include "mysql.h"
26 # include "errmsg.h"
27 # include "mysqld_error.h"
28 #elif defined(HAVE_ORACLE)
29 # include "oci.h"
30 #elif defined(HAVE_POSTGRESQL)
31 # include <libpq-fe.h>
32 #elif defined(HAVE_SQLITE3)
33 # include <sqlite3.h>
34 #endif
35
36 #include "dbschema.h"
37 #include "log.h"
38 #if defined(HAVE_SQLITE3)
39 # include "mutexs.h"
40 #endif
41
42 struct zbx_db_result
43 {
44 #if defined(HAVE_MYSQL)
45 MYSQL_RES *result;
46 #elif defined(HAVE_ORACLE)
47 OCIStmt *stmthp; /* the statement handle for select operations */
48 int ncolumn;
49 DB_ROW values;
50 ub4 *values_alloc;
51 OCILobLocator **clobs;
52 #elif defined(HAVE_POSTGRESQL)
53 PGresult *pg_result;
54 int row_num;
55 int fld_num;
56 int cursor;
57 DB_ROW values;
58 #elif defined(HAVE_SQLITE3)
59 int curow;
60 char **data;
61 int nrow;
62 int ncolumn;
63 DB_ROW values;
64 #endif
65 };
66
67 static int txn_level = 0; /* transaction level, nested transactions are not supported */
68 static int txn_error = ZBX_DB_OK; /* failed transaction */
69 static int txn_end_error = ZBX_DB_OK; /* transaction result */
70
71 static char *last_db_strerror = NULL; /* last database error message */
72
73 extern int CONFIG_LOG_SLOW_QUERIES;
74
75 static int db_auto_increment;
76
77 #if defined(HAVE_MYSQL)
78 static MYSQL *conn = NULL;
79 static zbx_uint32_t ZBX_MYSQL_SVERSION = ZBX_DBVERSION_UNDEFINED;
80 static int ZBX_MARIADB_SFORK = OFF;
81 #elif defined(HAVE_ORACLE)
82 #include "zbxalgo.h"
83
84 typedef struct
85 {
86 OCIEnv *envhp;
87 OCIError *errhp;
88 OCISvcCtx *svchp;
89 OCIServer *srvhp;
90 OCIStmt *stmthp; /* the statement handle for execute operations */
91 zbx_vector_ptr_t db_results;
92 }
93 zbx_oracle_db_handle_t;
94
95 static zbx_uint32_t ZBX_ORACLE_SVERSION = ZBX_DBVERSION_UNDEFINED;
96
97 static zbx_oracle_db_handle_t oracle;
98
99 static ub4 OCI_DBserver_status(void);
100
101 #elif defined(HAVE_POSTGRESQL)
102 static PGconn *conn = NULL;
103 static unsigned int ZBX_PG_BYTEAOID = 0;
104 static int ZBX_TSDB_VERSION = -1;
105 static zbx_uint32_t ZBX_PG_SVERSION = ZBX_DBVERSION_UNDEFINED;
106 char ZBX_PG_ESCAPE_BACKSLASH = 1;
107 #elif defined(HAVE_SQLITE3)
108 static sqlite3 *conn = NULL;
109 static zbx_mutex_t sqlite_access = ZBX_MUTEX_NULL;
110 #endif
111
112 #if defined(HAVE_ORACLE)
113 static void OCI_DBclean_result(DB_RESULT result);
114 #endif
115
zbx_db_errlog(zbx_err_codes_t zbx_errno,int db_errno,const char * db_error,const char * context)116 static void zbx_db_errlog(zbx_err_codes_t zbx_errno, int db_errno, const char *db_error, const char *context)
117 {
118 char *s;
119
120 if (NULL != db_error)
121 last_db_strerror = zbx_strdup(last_db_strerror, db_error);
122 else
123 last_db_strerror = zbx_strdup(last_db_strerror, "");
124
125 switch (zbx_errno)
126 {
127 case ERR_Z3001:
128 s = zbx_dsprintf(NULL, "connection to database '%s' failed: [%d] %s", context, db_errno,
129 last_db_strerror);
130 break;
131 case ERR_Z3002:
132 s = zbx_dsprintf(NULL, "cannot create database '%s': [%d] %s", context, db_errno,
133 last_db_strerror);
134 break;
135 case ERR_Z3003:
136 s = zbx_strdup(NULL, "no connection to the database");
137 break;
138 case ERR_Z3004:
139 s = zbx_dsprintf(NULL, "cannot close database: [%d] %s", db_errno, last_db_strerror);
140 break;
141 case ERR_Z3005:
142 s = zbx_dsprintf(NULL, "query failed: [%d] %s [%s]", db_errno, last_db_strerror, context);
143 break;
144 case ERR_Z3006:
145 s = zbx_dsprintf(NULL, "fetch failed: [%d] %s", db_errno, last_db_strerror);
146 break;
147 case ERR_Z3007:
148 s = zbx_dsprintf(NULL, "query failed: [%d] %s", db_errno, last_db_strerror);
149 break;
150 default:
151 s = zbx_strdup(NULL, "unknown error");
152 }
153
154 zabbix_log(LOG_LEVEL_ERR, "[Z%04d] %s", (int)zbx_errno, s);
155
156 zbx_free(s);
157 }
158
159 /******************************************************************************
160 * *
161 * Function: zbx_db_last_strerr *
162 * *
163 * Purpose: get last error set by database *
164 * *
165 * Return value: last database error message *
166 * *
167 ******************************************************************************/
168
zbx_db_last_strerr(void)169 const char *zbx_db_last_strerr(void)
170 {
171 return last_db_strerror;
172 }
173
174 #if defined(HAVE_ORACLE)
zbx_oci_error(sword status,sb4 * err)175 static const char *zbx_oci_error(sword status, sb4 *err)
176 {
177 static char errbuf[512];
178 sb4 errcode, *perrcode;
179
180 perrcode = (NULL == err ? &errcode : err);
181
182 errbuf[0] = '\0';
183 *perrcode = 0;
184
185 switch (status)
186 {
187 case OCI_SUCCESS_WITH_INFO:
188 OCIErrorGet((dvoid *)oracle.errhp, (ub4)1, (text *)NULL, perrcode,
189 (text *)errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
190 break;
191 case OCI_NEED_DATA:
192 zbx_snprintf(errbuf, sizeof(errbuf), "%s", "OCI_NEED_DATA");
193 break;
194 case OCI_NO_DATA:
195 zbx_snprintf(errbuf, sizeof(errbuf), "%s", "OCI_NODATA");
196 break;
197 case OCI_ERROR:
198 OCIErrorGet((dvoid *)oracle.errhp, (ub4)1, (text *)NULL, perrcode,
199 (text *)errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
200 break;
201 case OCI_INVALID_HANDLE:
202 zbx_snprintf(errbuf, sizeof(errbuf), "%s", "OCI_INVALID_HANDLE");
203 break;
204 case OCI_STILL_EXECUTING:
205 zbx_snprintf(errbuf, sizeof(errbuf), "%s", "OCI_STILL_EXECUTING");
206 break;
207 case OCI_CONTINUE:
208 zbx_snprintf(errbuf, sizeof(errbuf), "%s", "OCI_CONTINUE");
209 break;
210 }
211
212 zbx_rtrim(errbuf, ZBX_WHITESPACE);
213
214 return errbuf;
215 }
216
217 /******************************************************************************
218 * *
219 * Function: OCI_handle_sql_error *
220 * *
221 * Purpose: handles Oracle prepare/bind/execute/select operation error *
222 * *
223 * Parameters: zerrcode - [IN] the Zabbix errorcode for the failed database *
224 * operation *
225 * oci_error - [IN] the return code from failed Oracle operation *
226 * sql - [IN] the failed sql statement (can be NULL) *
227 * *
228 * Return value: ZBX_DB_DOWN - database connection is down *
229 * ZBX_DB_FAIL - otherwise *
230 * *
231 * Comments: This function logs the error description and checks the *
232 * database connection status. *
233 * *
234 ******************************************************************************/
OCI_handle_sql_error(int zerrcode,sword oci_error,const char * sql)235 static int OCI_handle_sql_error(int zerrcode, sword oci_error, const char *sql)
236 {
237 sb4 errcode;
238 int ret = ZBX_DB_DOWN;
239
240 zbx_db_errlog(zerrcode, oci_error, zbx_oci_error(oci_error, &errcode), sql);
241
242 /* after ORA-02396 (and consequent ORA-01012) errors the OCI_SERVER_NORMAL server status is still returned */
243 switch (errcode)
244 {
245 case 1012: /* ORA-01012: not logged on */
246 case 2396: /* ORA-02396: exceeded maximum idle time */
247 goto out;
248 }
249
250 if (OCI_SERVER_NORMAL == OCI_DBserver_status())
251 ret = ZBX_DB_FAIL;
252 out:
253 return ret;
254 }
255
256 #endif /* HAVE_ORACLE */
257
258 #ifdef HAVE_POSTGRESQL
zbx_postgresql_error(char ** error,const PGresult * pg_result)259 static void zbx_postgresql_error(char **error, const PGresult *pg_result)
260 {
261 char *result_error_msg;
262 size_t error_alloc = 0, error_offset = 0;
263
264 zbx_snprintf_alloc(error, &error_alloc, &error_offset, "%s", PQresStatus(PQresultStatus(pg_result)));
265
266 result_error_msg = PQresultErrorMessage(pg_result);
267
268 if ('\0' != *result_error_msg)
269 zbx_snprintf_alloc(error, &error_alloc, &error_offset, ":%s", result_error_msg);
270 }
271 #endif /*HAVE_POSTGRESQL*/
272
273 __zbx_attr_format_printf(1, 2)
zbx_db_execute(const char * fmt,...)274 static int zbx_db_execute(const char *fmt, ...)
275 {
276 va_list args;
277 int ret;
278
279 va_start(args, fmt);
280 ret = zbx_db_vexecute(fmt, args);
281 va_end(args);
282
283 return ret;
284 }
285
286 __zbx_attr_format_printf(1, 2)
zbx_db_select(const char * fmt,...)287 static DB_RESULT zbx_db_select(const char *fmt, ...)
288 {
289 va_list args;
290 DB_RESULT result;
291
292 va_start(args, fmt);
293 result = zbx_db_vselect(fmt, args);
294 va_end(args);
295
296 return result;
297 }
298
299 #if defined(HAVE_MYSQL)
is_recoverable_mysql_error(void)300 static int is_recoverable_mysql_error(void)
301 {
302 switch (mysql_errno(conn))
303 {
304 case CR_CONN_HOST_ERROR:
305 case CR_SERVER_GONE_ERROR:
306 case CR_CONNECTION_ERROR:
307 case CR_SERVER_LOST:
308 case CR_UNKNOWN_HOST:
309 case CR_COMMANDS_OUT_OF_SYNC:
310 case ER_SERVER_SHUTDOWN:
311 case ER_ACCESS_DENIED_ERROR: /* wrong user or password */
312 case ER_ILLEGAL_GRANT_FOR_TABLE: /* user without any privileges */
313 case ER_TABLEACCESS_DENIED_ERROR: /* user without some privilege */
314 case ER_UNKNOWN_ERROR:
315 case ER_UNKNOWN_COM_ERROR:
316 case ER_LOCK_DEADLOCK:
317 case ER_LOCK_WAIT_TIMEOUT:
318 #ifdef CR_SSL_CONNECTION_ERROR
319 case CR_SSL_CONNECTION_ERROR:
320 #endif
321 #ifdef ER_CONNECTION_KILLED
322 case ER_CONNECTION_KILLED:
323 #endif
324 return SUCCEED;
325 }
326
327 return FAIL;
328 }
329 #elif defined(HAVE_POSTGRESQL)
is_recoverable_postgresql_error(const PGconn * pg_conn,const PGresult * pg_result)330 static int is_recoverable_postgresql_error(const PGconn *pg_conn, const PGresult *pg_result)
331 {
332 if (CONNECTION_OK != PQstatus(pg_conn))
333 return SUCCEED;
334
335 if (0 == zbx_strcmp_null(PQresultErrorField(pg_result, PG_DIAG_SQLSTATE), "40P01"))
336 return SUCCEED;
337
338 return FAIL;
339 }
340 #endif
341
342 /******************************************************************************
343 * *
344 * Function: zbx_db_init_autoincrement_options *
345 * *
346 * Purpose: specify the autoincrement options during db connect *
347 * *
348 ******************************************************************************/
zbx_db_init_autoincrement_options(void)349 void zbx_db_init_autoincrement_options(void)
350 {
351 db_auto_increment = 1;
352 }
353
354 /******************************************************************************
355 * *
356 * Function: zbx_db_connect *
357 * *
358 * Purpose: connect to the database *
359 * *
360 * Return value: ZBX_DB_OK - successfully connected *
361 * ZBX_DB_DOWN - database is down *
362 * ZBX_DB_FAIL - failed to connect *
363 * *
364 ******************************************************************************/
zbx_db_connect(char * host,char * user,char * password,char * dbname,char * dbschema,char * dbsocket,int port,char * tls_connect,char * cert,char * key,char * ca,char * cipher,char * cipher_13)365 int zbx_db_connect(char *host, char *user, char *password, char *dbname, char *dbschema, char *dbsocket, int port,
366 char *tls_connect, char *cert, char *key, char *ca, char *cipher, char *cipher_13)
367 {
368 int ret = ZBX_DB_OK, last_txn_error, last_txn_level;
369 #if defined(HAVE_MYSQL)
370 #if LIBMYSQL_VERSION_ID >= 80000 /* my_bool type is removed in MySQL 8.0 */
371 bool mysql_reconnect = 1;
372 #else
373 my_bool mysql_reconnect = 1;
374 #endif
375 #elif defined(HAVE_ORACLE)
376 char *connect = NULL;
377 sword err = OCI_SUCCESS;
378 static ub2 csid = 0;
379 #elif defined(HAVE_POSTGRESQL)
380 # define ZBX_DB_MAX_PARAMS 9
381
382 int rc;
383 char *cport = NULL;
384 DB_RESULT result;
385 DB_ROW row;
386 const char *keywords[ZBX_DB_MAX_PARAMS + 1];
387 const char *values[ZBX_DB_MAX_PARAMS + 1];
388 unsigned int i = 0;
389 #elif defined(HAVE_SQLITE3)
390 char *p, *path = NULL;
391 #endif
392
393 #ifndef HAVE_MYSQL
394 ZBX_UNUSED(dbsocket);
395 #endif
396 /* Allow executing statements during a connection initialization. Make sure to mark transaction as failed. */
397 if (0 != txn_level)
398 txn_error = ZBX_DB_DOWN;
399
400 last_txn_error = txn_error;
401 last_txn_level = txn_level;
402
403 txn_error = ZBX_DB_OK;
404 txn_level = 0;
405
406 #if defined(HAVE_MYSQL)
407 ZBX_UNUSED(dbschema);
408
409 if (NULL == (conn = mysql_init(NULL)))
410 {
411 zabbix_log(LOG_LEVEL_CRIT, "cannot allocate or initialize MYSQL database connection object");
412 exit(EXIT_FAILURE);
413 }
414
415 if (1 == db_auto_increment)
416 {
417 /* Shadow global auto_increment variables. */
418 /* Setting session variables requires special permissions in MySQL 8.0.14-8.0.17. */
419
420 if (0 != MYSQL_OPTIONS(conn, MYSQL_INIT_COMMAND, MYSQL_OPTIONS_ARGS_VOID_CAST
421 "set @@session.auto_increment_increment=1"))
422 {
423 zabbix_log(LOG_LEVEL_ERR, "Cannot set auto_increment_increment option.");
424 ret = ZBX_DB_FAIL;
425 }
426
427 if (ZBX_DB_OK == ret && 0 != MYSQL_OPTIONS(conn, MYSQL_INIT_COMMAND, MYSQL_OPTIONS_ARGS_VOID_CAST
428 "set @@session.auto_increment_offset=1"))
429 {
430 zabbix_log(LOG_LEVEL_ERR, "Cannot set auto_increment_offset option.");
431 ret = ZBX_DB_FAIL;
432 }
433 }
434
435 #if defined(HAVE_MYSQL_TLS)
436 if (ZBX_DB_OK == ret && NULL != tls_connect)
437 {
438 unsigned int mysql_tls_mode;
439
440 if (0 == strcmp(tls_connect, ZBX_DB_TLS_CONNECT_REQUIRED_TXT))
441 mysql_tls_mode = SSL_MODE_REQUIRED;
442 else if (0 == strcmp(tls_connect, ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT))
443 mysql_tls_mode = SSL_MODE_VERIFY_CA;
444 else
445 mysql_tls_mode = SSL_MODE_VERIFY_IDENTITY;
446
447 if (0 != mysql_options(conn, MYSQL_OPT_SSL_MODE, &mysql_tls_mode))
448 {
449 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_MODE option.");
450 ret = ZBX_DB_FAIL;
451 }
452 }
453
454 if (ZBX_DB_OK == ret && NULL != ca && 0 != mysql_options(conn, MYSQL_OPT_SSL_CA, ca))
455 {
456 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CA option.");
457 ret = ZBX_DB_FAIL;
458 }
459
460 if (ZBX_DB_OK == ret && NULL != key && 0 != mysql_options(conn, MYSQL_OPT_SSL_KEY, key))
461 {
462 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_KEY option.");
463 ret = ZBX_DB_FAIL;
464 }
465
466 if (ZBX_DB_OK == ret && NULL != cert && 0 != mysql_options(conn, MYSQL_OPT_SSL_CERT, cert))
467 {
468 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CERT option.");
469 ret = ZBX_DB_FAIL;
470 }
471
472 if (ZBX_DB_OK == ret && NULL != cipher && 0 != mysql_options(conn, MYSQL_OPT_SSL_CIPHER, cipher))
473 {
474 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CIPHER option.");
475 ret = ZBX_DB_FAIL;
476 }
477 #if defined(HAVE_MYSQL_TLS_CIPHERSUITES)
478 if (ZBX_DB_OK == ret && NULL != cipher_13 && 0 != mysql_options(conn, MYSQL_OPT_TLS_CIPHERSUITES, cipher_13))
479 {
480 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_TLS_CIPHERSUITES option.");
481 ret = ZBX_DB_FAIL;
482 }
483 #else
484 ZBX_UNUSED(cipher_13);
485 #endif
486 #elif defined(HAVE_MARIADB_TLS)
487 ZBX_UNUSED(cipher_13);
488
489 if (ZBX_DB_OK == ret && NULL != tls_connect)
490 {
491 if (0 == strcmp(tls_connect, ZBX_DB_TLS_CONNECT_REQUIRED_TXT))
492 {
493 my_bool enforce_tls = 1;
494
495 if (0 != mysql_optionsv(conn, MYSQL_OPT_SSL_ENFORCE, (void *)&enforce_tls))
496 {
497 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_ENFORCE option.");
498 ret = ZBX_DB_FAIL;
499 }
500 }
501 else
502 {
503 my_bool verify = 1;
504
505 if (0 != mysql_optionsv(conn, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (void *)&verify))
506 {
507 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_VERIFY_SERVER_CERT option.");
508 ret = ZBX_DB_FAIL;
509 }
510 }
511 }
512
513 if (ZBX_DB_OK == ret && NULL != ca && 0 != mysql_optionsv(conn, MYSQL_OPT_SSL_CA, ca))
514 {
515 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CA option.");
516 ret = ZBX_DB_FAIL;
517 }
518
519 if (ZBX_DB_OK == ret && NULL != key && 0 != mysql_optionsv(conn, MYSQL_OPT_SSL_KEY, key))
520 {
521 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_KEY option.");
522 ret = ZBX_DB_FAIL;
523 }
524
525 if (ZBX_DB_OK == ret && NULL != cert && 0 != mysql_optionsv(conn, MYSQL_OPT_SSL_CERT, cert))
526 {
527 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CERT option.");
528 ret = ZBX_DB_FAIL;
529 }
530
531 if (ZBX_DB_OK == ret && NULL != cipher && 0 != mysql_optionsv(conn, MYSQL_OPT_SSL_CIPHER, cipher))
532 {
533 zabbix_log(LOG_LEVEL_ERR, "Cannot set MYSQL_OPT_SSL_CIPHER option.");
534 ret = ZBX_DB_FAIL;
535 }
536 #else
537 ZBX_UNUSED(tls_connect);
538 ZBX_UNUSED(cert);
539 ZBX_UNUSED(key);
540 ZBX_UNUSED(ca);
541 ZBX_UNUSED(cipher);
542 ZBX_UNUSED(cipher_13);
543 #endif
544 if (ZBX_DB_OK == ret &&
545 NULL == mysql_real_connect(conn, host, user, password, dbname, port, dbsocket,
546 CLIENT_MULTI_STATEMENTS))
547 {
548 zbx_db_errlog(ERR_Z3001, mysql_errno(conn), mysql_error(conn), dbname);
549 ret = ZBX_DB_FAIL;
550 }
551
552 /* The RECONNECT option setting is placed here, AFTER the connection */
553 /* is made, due to a bug in MySQL versions prior to 5.1.6 where it */
554 /* reset the options value to the default, regardless of what it was */
555 /* set to prior to the connection. MySQL allows changing connection */
556 /* options on an open connection, so setting it here is safe. */
557
558 if (ZBX_DB_OK == ret && 0 != mysql_options(conn, MYSQL_OPT_RECONNECT, &mysql_reconnect))
559 zabbix_log(LOG_LEVEL_WARNING, "Cannot set MySQL reconnect option.");
560
561 /* in contrast to "set names utf8" results of this call will survive auto-reconnects */
562 if (ZBX_DB_OK == ret && 0 != mysql_set_character_set(conn, "utf8"))
563 zabbix_log(LOG_LEVEL_WARNING, "cannot set MySQL character set to \"utf8\"");
564
565 if (ZBX_DB_OK == ret && 0 != mysql_autocommit(conn, 1))
566 {
567 zbx_db_errlog(ERR_Z3001, mysql_errno(conn), mysql_error(conn), dbname);
568 ret = ZBX_DB_FAIL;
569 }
570
571 if (ZBX_DB_OK == ret && 0 != mysql_select_db(conn, dbname))
572 {
573 zbx_db_errlog(ERR_Z3001, mysql_errno(conn), mysql_error(conn), dbname);
574 ret = ZBX_DB_FAIL;
575 }
576
577 if (ZBX_DB_FAIL == ret && SUCCEED == is_recoverable_mysql_error())
578 ret = ZBX_DB_DOWN;
579
580 #elif defined(HAVE_ORACLE)
581 ZBX_UNUSED(dbschema);
582 ZBX_UNUSED(tls_connect);
583 ZBX_UNUSED(cert);
584 ZBX_UNUSED(key);
585 ZBX_UNUSED(ca);
586 ZBX_UNUSED(cipher);
587 ZBX_UNUSED(cipher_13);
588
589 memset(&oracle, 0, sizeof(oracle));
590
591 zbx_vector_ptr_create(&oracle.db_results);
592
593 /* connection string format: [//]host[:port][/service name] */
594
595 if ('\0' != *host)
596 {
597 /* Easy Connect method */
598 connect = zbx_strdcatf(connect, "//%s", host);
599 if (0 != port)
600 connect = zbx_strdcatf(connect, ":%d", port);
601 if ('\0' != *dbname)
602 connect = zbx_strdcatf(connect, "/%s", dbname);
603 }
604 else
605 {
606 /* Net Service Name method */
607 connect = zbx_strdup(connect, dbname);
608 }
609
610 while (ZBX_DB_OK == ret)
611 {
612 /* initialize environment */
613 if (OCI_SUCCESS == (err = OCIEnvNlsCreate((OCIEnv **)&oracle.envhp, (ub4)OCI_DEFAULT, (dvoid *)0,
614 (dvoid * (*)(dvoid *,size_t))0, (dvoid * (*)(dvoid *, dvoid *, size_t))0,
615 (void (*)(dvoid *, dvoid *))0, (size_t)0, (dvoid **)0, csid, csid)))
616 {
617 if (0 != csid)
618 break; /* environment with UTF8 character set successfully created */
619
620 /* try to find out the id of UTF8 character set */
621 if (0 == (csid = OCINlsCharSetNameToId(oracle.envhp, (const oratext *)"UTF8")))
622 {
623 zabbix_log(LOG_LEVEL_WARNING, "Cannot find out the ID of \"UTF8\" character set."
624 " Relying on current \"NLS_LANG\" settings.");
625 break; /* use default environment with character set derived from NLS_LANG */
626 }
627
628 /* get rid of this environment to create a better one on the next iteration */
629 OCIHandleFree((dvoid *)oracle.envhp, OCI_HTYPE_ENV);
630 oracle.envhp = NULL;
631 }
632 else
633 {
634 zbx_db_errlog(ERR_Z3001, err, zbx_oci_error(err, NULL), connect);
635 ret = ZBX_DB_FAIL;
636 }
637 }
638
639 if (ZBX_DB_OK == ret)
640 {
641 /* allocate an error handle */
642 (void)OCIHandleAlloc((dvoid *)oracle.envhp, (dvoid **)&oracle.errhp, OCI_HTYPE_ERROR,
643 (size_t)0, (dvoid **)0);
644
645 /* get the session */
646 err = OCILogon2(oracle.envhp, oracle.errhp, &oracle.svchp,
647 (text *)user, (ub4)(NULL != user ? strlen(user) : 0),
648 (text *)password, (ub4)(NULL != password ? strlen(password) : 0),
649 (text *)connect, (ub4)strlen(connect),
650 OCI_DEFAULT);
651
652 switch (err)
653 {
654 case OCI_SUCCESS_WITH_INFO:
655 zabbix_log(LOG_LEVEL_WARNING, "%s", zbx_oci_error(err, NULL));
656 /* break; is not missing here */
657 ZBX_FALLTHROUGH;
658 case OCI_SUCCESS:
659 err = OCIAttrGet((void *)oracle.svchp, OCI_HTYPE_SVCCTX, (void *)&oracle.srvhp,
660 (ub4 *)0, OCI_ATTR_SERVER, oracle.errhp);
661 }
662
663 if (OCI_SUCCESS != err)
664 {
665 zbx_db_errlog(ERR_Z3001, err, zbx_oci_error(err, NULL), connect);
666 ret = ZBX_DB_DOWN;
667 }
668 }
669
670 if (ZBX_DB_OK == ret)
671 {
672 /* initialize statement handle */
673 err = OCIHandleAlloc((dvoid *)oracle.envhp, (dvoid **)&oracle.stmthp, OCI_HTYPE_STMT,
674 (size_t)0, (dvoid **)0);
675
676 if (OCI_SUCCESS != err)
677 {
678 zbx_db_errlog(ERR_Z3001, err, zbx_oci_error(err, NULL), connect);
679 ret = ZBX_DB_DOWN;
680 }
681 }
682
683 if (ZBX_DB_OK == ret)
684 {
685 if (0 < (ret = zbx_db_execute("alter session set nls_numeric_characters='. '")))
686 ret = ZBX_DB_OK;
687 }
688
689 zbx_free(connect);
690 #elif defined(HAVE_POSTGRESQL)
691 ZBX_UNUSED(cipher);
692 ZBX_UNUSED(cipher_13);
693
694 if (NULL != tls_connect)
695 {
696 keywords[i] = "sslmode";
697
698 if (0 == strcmp(tls_connect, ZBX_DB_TLS_CONNECT_REQUIRED_TXT))
699 values[i++] = "require";
700 else if (0 == strcmp(tls_connect, ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT))
701 values[i++] = "verify-ca";
702 else
703 values[i++] = "verify-full";
704 }
705
706 if (NULL != cert)
707 {
708 keywords[i] = "sslcert";
709 values[i++] = cert;
710 }
711
712 if (NULL != key)
713 {
714 keywords[i] = "sslkey";
715 values[i++] = key;
716 }
717
718 if (NULL != ca)
719 {
720 keywords[i] = "sslrootcert";
721 values[i++] = ca;
722 }
723
724 if (NULL != host)
725 {
726 keywords[i] = "host";
727 values[i++] = host;
728 }
729
730 if (NULL != dbname)
731 {
732 keywords[i] = "dbname";
733 values[i++] = dbname;
734 }
735
736 if (NULL != user)
737 {
738 keywords[i] = "user";
739 values[i++] = user;
740 }
741
742 if (NULL != password)
743 {
744 keywords[i] = "password";
745 values[i++] = password;
746 }
747
748 if (0 != port)
749 {
750 keywords[i] = "port";
751 values[i++] = cport = zbx_dsprintf(cport, "%d", port);
752 }
753
754 keywords[i] = NULL;
755 values[i] = NULL;
756
757 conn = PQconnectdbParams(keywords, values, 0);
758
759 zbx_free(cport);
760
761 /* check to see that the backend connection was successfully made */
762 if (CONNECTION_OK != PQstatus(conn))
763 {
764 zbx_db_errlog(ERR_Z3001, 0, PQerrorMessage(conn), dbname);
765 ret = ZBX_DB_DOWN;
766 goto out;
767 }
768
769 if (NULL != dbschema && '\0' != *dbschema)
770 {
771 char *dbschema_esc;
772
773 dbschema_esc = zbx_db_dyn_escape_string(dbschema, ZBX_SIZE_T_MAX, ZBX_SIZE_T_MAX, ESCAPE_SEQUENCE_ON);
774 if (ZBX_DB_DOWN == (rc = zbx_db_execute("set schema '%s'", dbschema_esc)) || ZBX_DB_FAIL == rc)
775 ret = rc;
776 zbx_free(dbschema_esc);
777 }
778
779 if (ZBX_DB_FAIL == ret || ZBX_DB_DOWN == ret)
780 goto out;
781
782 result = zbx_db_select("select oid from pg_type where typname='bytea'");
783
784 if ((DB_RESULT)ZBX_DB_DOWN == result || NULL == result)
785 {
786 ret = (NULL == result) ? ZBX_DB_FAIL : ZBX_DB_DOWN;
787 goto out;
788 }
789
790 if (NULL != (row = zbx_db_fetch(result)))
791 ZBX_PG_BYTEAOID = atoi(row[0]);
792 DBfree_result(result);
793
794 /* disable "nonstandard use of \' in a string literal" warning */
795 if (0 < (ret = zbx_db_execute("set escape_string_warning to off")))
796 ret = ZBX_DB_OK;
797
798 if (ZBX_DB_OK != ret)
799 goto out;
800
801 /* increase float precision */
802 if (0 < (ret = zbx_db_execute("set extra_float_digits to 3")))
803 ret = ZBX_DB_OK;
804
805 if (ZBX_DB_OK != ret)
806 goto out;
807
808 result = zbx_db_select("show standard_conforming_strings");
809
810 if ((DB_RESULT)ZBX_DB_DOWN == result || NULL == result)
811 {
812 ret = (NULL == result) ? ZBX_DB_FAIL : ZBX_DB_DOWN;
813 goto out;
814 }
815
816 if (NULL != (row = zbx_db_fetch(result)))
817 ZBX_PG_ESCAPE_BACKSLASH = (0 == strcmp(row[0], "off"));
818 DBfree_result(result);
819
820 if (90000 <= ZBX_PG_SVERSION)
821 {
822 /* change the output format for values of type bytea from hex (the default) to escape */
823 if (0 < (ret = zbx_db_execute("set bytea_output=escape")))
824 ret = ZBX_DB_OK;
825 }
826 out:
827 #elif defined(HAVE_SQLITE3)
828 ZBX_UNUSED(host);
829 ZBX_UNUSED(user);
830 ZBX_UNUSED(password);
831 ZBX_UNUSED(dbschema);
832 ZBX_UNUSED(port);
833 ZBX_UNUSED(tls_connect);
834 ZBX_UNUSED(cert);
835 ZBX_UNUSED(key);
836 ZBX_UNUSED(ca);
837 ZBX_UNUSED(cipher);
838 ZBX_UNUSED(cipher_13);
839 #ifdef HAVE_FUNCTION_SQLITE3_OPEN_V2
840 if (SQLITE_OK != sqlite3_open_v2(dbname, &conn, SQLITE_OPEN_READWRITE, NULL))
841 #else
842 if (SQLITE_OK != sqlite3_open(dbname, &conn))
843 #endif
844 {
845 zbx_db_errlog(ERR_Z3001, 0, sqlite3_errmsg(conn), dbname);
846 ret = ZBX_DB_DOWN;
847 goto out;
848 }
849
850 /* do not return SQLITE_BUSY immediately, wait for N ms */
851 sqlite3_busy_timeout(conn, SEC_PER_MIN * 1000);
852
853 if (0 < (ret = zbx_db_execute("pragma synchronous=0")))
854 ret = ZBX_DB_OK;
855
856 if (ZBX_DB_OK != ret)
857 goto out;
858
859 if (0 < (ret = zbx_db_execute("pragma temp_store=2")))
860 ret = ZBX_DB_OK;
861
862 if (ZBX_DB_OK != ret)
863 goto out;
864
865 path = zbx_strdup(NULL, dbname);
866
867 if (NULL != (p = strrchr(path, '/')))
868 *++p = '\0';
869 else
870 *path = '\0';
871
872 if (0 < (ret = zbx_db_execute("pragma temp_store_directory='%s'", path)))
873 ret = ZBX_DB_OK;
874
875 zbx_free(path);
876 out:
877 #endif /* HAVE_SQLITE3 */
878 if (ZBX_DB_OK != ret)
879 zbx_db_close();
880
881 txn_error = last_txn_error;
882 txn_level = last_txn_level;
883
884 return ret;
885 }
886
zbx_db_init(const char * dbname,const char * const dbschema,char ** error)887 int zbx_db_init(const char *dbname, const char *const dbschema, char **error)
888 {
889 #ifdef HAVE_SQLITE3
890 zbx_stat_t buf;
891
892 if (0 != zbx_stat(dbname, &buf))
893 {
894 zabbix_log(LOG_LEVEL_WARNING, "cannot open database file \"%s\": %s", dbname, zbx_strerror(errno));
895 zabbix_log(LOG_LEVEL_WARNING, "creating database ...");
896
897 if (SQLITE_OK != sqlite3_open(dbname, &conn))
898 {
899 zbx_db_errlog(ERR_Z3002, 0, sqlite3_errmsg(conn), dbname);
900 *error = zbx_strdup(*error, "cannot open database");
901 return FAIL;
902 }
903
904 if (SUCCEED != zbx_mutex_create(&sqlite_access, ZBX_MUTEX_SQLITE3, error))
905 return FAIL;
906
907 zbx_db_execute("%s", dbschema);
908 zbx_db_close();
909 return SUCCEED;
910 }
911
912 return zbx_mutex_create(&sqlite_access, ZBX_MUTEX_SQLITE3, error);
913 #else /* not HAVE_SQLITE3 */
914 ZBX_UNUSED(dbname);
915 ZBX_UNUSED(dbschema);
916 ZBX_UNUSED(error);
917
918 return SUCCEED;
919 #endif /* HAVE_SQLITE3 */
920 }
921
zbx_db_deinit(void)922 void zbx_db_deinit(void)
923 {
924 #ifdef HAVE_SQLITE3
925 zbx_mutex_destroy(&sqlite_access);
926 #endif
927 }
928
zbx_db_close(void)929 void zbx_db_close(void)
930 {
931 #if defined(HAVE_MYSQL)
932 if (NULL != conn)
933 {
934 mysql_close(conn);
935 conn = NULL;
936 }
937 #elif defined(HAVE_ORACLE)
938 if (0 != oracle.db_results.values_num)
939 {
940 int i;
941
942 zabbix_log(LOG_LEVEL_WARNING, "cannot process queries: database is closed");
943
944 for (i = 0; i < oracle.db_results.values_num; i++)
945 {
946 /* deallocate all handles before environment is deallocated */
947 OCI_DBclean_result(oracle.db_results.values[i]);
948 }
949 }
950
951 /* deallocate statement handle */
952 if (NULL != oracle.stmthp)
953 {
954 OCIHandleFree((dvoid *)oracle.stmthp, OCI_HTYPE_STMT);
955 oracle.stmthp = NULL;
956 }
957
958 if (NULL != oracle.svchp)
959 {
960 OCILogoff(oracle.svchp, oracle.errhp);
961 oracle.svchp = NULL;
962 }
963
964 if (NULL != oracle.errhp)
965 {
966 OCIHandleFree(oracle.errhp, OCI_HTYPE_ERROR);
967 oracle.errhp = NULL;
968 }
969
970 if (NULL != oracle.srvhp)
971 {
972 OCIHandleFree(oracle.srvhp, OCI_HTYPE_SERVER);
973 oracle.srvhp = NULL;
974 }
975
976 if (NULL != oracle.envhp)
977 {
978 /* delete the environment handle, which deallocates all other handles associated with it */
979 OCIHandleFree((dvoid *)oracle.envhp, OCI_HTYPE_ENV);
980 oracle.envhp = NULL;
981 }
982
983 zbx_vector_ptr_destroy(&oracle.db_results);
984 #elif defined(HAVE_POSTGRESQL)
985 if (NULL != conn)
986 {
987 PQfinish(conn);
988 conn = NULL;
989 }
990 #elif defined(HAVE_SQLITE3)
991 if (NULL != conn)
992 {
993 sqlite3_close(conn);
994 conn = NULL;
995 }
996 #endif
997 }
998
999 /******************************************************************************
1000 * *
1001 * Function: zbx_db_begin *
1002 * *
1003 * Purpose: start transaction *
1004 * *
1005 * Comments: do nothing if DB does not support transactions *
1006 * *
1007 ******************************************************************************/
zbx_db_begin(void)1008 int zbx_db_begin(void)
1009 {
1010 int rc = ZBX_DB_OK;
1011
1012 if (txn_level > 0)
1013 {
1014 zabbix_log(LOG_LEVEL_CRIT, "ERROR: nested transaction detected. Please report it to Zabbix Team.");
1015 assert(0);
1016 }
1017
1018 txn_level++;
1019
1020 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
1021 rc = zbx_db_execute("begin;");
1022 #elif defined(HAVE_SQLITE3)
1023 zbx_mutex_lock(sqlite_access);
1024 rc = zbx_db_execute("begin;");
1025 #endif
1026
1027 if (ZBX_DB_DOWN == rc)
1028 txn_level--;
1029
1030 return rc;
1031 }
1032
1033 /******************************************************************************
1034 * *
1035 * Function: zbx_db_commit *
1036 * *
1037 * Purpose: commit transaction *
1038 * *
1039 * Comments: do nothing if DB does not support transactions *
1040 * *
1041 ******************************************************************************/
zbx_db_commit(void)1042 int zbx_db_commit(void)
1043 {
1044 int rc = ZBX_DB_OK;
1045 #ifdef HAVE_ORACLE
1046 sword err;
1047 #endif
1048
1049 if (0 == txn_level)
1050 {
1051 zabbix_log(LOG_LEVEL_CRIT, "ERROR: commit without transaction."
1052 " Please report it to Zabbix Team.");
1053 assert(0);
1054 }
1055
1056 if (ZBX_DB_OK != txn_error)
1057 return ZBX_DB_FAIL; /* commit called on failed transaction */
1058
1059 #if defined(HAVE_ORACLE)
1060 if (OCI_SUCCESS != (err = OCITransCommit(oracle.svchp, oracle.errhp, OCI_DEFAULT)))
1061 rc = OCI_handle_sql_error(ERR_Z3005, err, "commit failed");
1062 #elif defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL) || defined(HAVE_SQLITE3)
1063 rc = zbx_db_execute("commit;");
1064 #endif
1065
1066 if (ZBX_DB_OK > rc) { /* commit failed */
1067 txn_error = rc;
1068 return rc;
1069 }
1070
1071 #ifdef HAVE_SQLITE3
1072 zbx_mutex_unlock(sqlite_access);
1073 #endif
1074
1075 txn_level--;
1076 txn_end_error = ZBX_DB_OK;
1077
1078 return rc;
1079 }
1080
1081 /******************************************************************************
1082 * *
1083 * Function: zbx_db_rollback *
1084 * *
1085 * Purpose: rollback transaction *
1086 * *
1087 * Comments: do nothing if DB does not support transactions *
1088 * *
1089 ******************************************************************************/
zbx_db_rollback(void)1090 int zbx_db_rollback(void)
1091 {
1092 int rc = ZBX_DB_OK, last_txn_error;
1093 #ifdef HAVE_ORACLE
1094 sword err;
1095 #endif
1096
1097 if (0 == txn_level)
1098 {
1099 zabbix_log(LOG_LEVEL_CRIT, "ERROR: rollback without transaction."
1100 " Please report it to Zabbix Team.");
1101 assert(0);
1102 }
1103
1104 last_txn_error = txn_error;
1105
1106 /* allow rollback of failed transaction */
1107 txn_error = ZBX_DB_OK;
1108
1109 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
1110 rc = zbx_db_execute("rollback;");
1111 #elif defined(HAVE_ORACLE)
1112 if (OCI_SUCCESS != (err = OCITransRollback(oracle.svchp, oracle.errhp, OCI_DEFAULT)))
1113 rc = OCI_handle_sql_error(ERR_Z3005, err, "rollback failed");
1114 #elif defined(HAVE_SQLITE3)
1115 rc = zbx_db_execute("rollback;");
1116 zbx_mutex_unlock(sqlite_access);
1117 #endif
1118
1119 /* There is no way to recover from rollback errors, so there is no need to preserve transaction level / error. */
1120 txn_level = 0;
1121 txn_error = ZBX_DB_OK;
1122
1123 if (ZBX_DB_FAIL == rc)
1124 txn_end_error = ZBX_DB_FAIL;
1125 else
1126 txn_end_error = last_txn_error; /* error that caused rollback */
1127
1128 return rc;
1129 }
1130
zbx_db_txn_level(void)1131 int zbx_db_txn_level(void)
1132 {
1133 return txn_level;
1134 }
1135
zbx_db_txn_error(void)1136 int zbx_db_txn_error(void)
1137 {
1138 return txn_error;
1139 }
1140
zbx_db_txn_end_error(void)1141 int zbx_db_txn_end_error(void)
1142 {
1143 return txn_end_error;
1144 }
1145
1146 #ifdef HAVE_ORACLE
zbx_oracle_statement_prepare(const char * sql)1147 static sword zbx_oracle_statement_prepare(const char *sql)
1148 {
1149 return OCIStmtPrepare(oracle.stmthp, oracle.errhp, (text *)sql, (ub4)strlen((char *)sql), (ub4)OCI_NTV_SYNTAX,
1150 (ub4)OCI_DEFAULT);
1151 }
1152
zbx_oracle_statement_execute(ub4 iters,ub4 * nrows)1153 static sword zbx_oracle_statement_execute(ub4 iters, ub4 *nrows)
1154 {
1155 sword err;
1156
1157 if (OCI_SUCCESS == (err = OCIStmtExecute(oracle.svchp, oracle.stmthp, oracle.errhp, iters, (ub4)0,
1158 (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL,
1159 0 == txn_level ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)))
1160 {
1161 err = OCIAttrGet((void *)oracle.stmthp, OCI_HTYPE_STMT, nrows, (ub4 *)0, OCI_ATTR_ROW_COUNT,
1162 oracle.errhp);
1163 }
1164
1165 return err;
1166 }
1167 #endif
1168
1169 #ifdef HAVE_ORACLE
zbx_db_statement_prepare(const char * sql)1170 int zbx_db_statement_prepare(const char *sql)
1171 {
1172 sword err;
1173 int ret = ZBX_DB_OK;
1174
1175 if (0 == txn_level)
1176 zabbix_log(LOG_LEVEL_DEBUG, "query without transaction detected");
1177
1178 if (ZBX_DB_OK != txn_error)
1179 {
1180 zabbix_log(LOG_LEVEL_DEBUG, "ignoring query [txnlev:%d] within failed transaction", txn_level);
1181 return ZBX_DB_FAIL;
1182 }
1183
1184 zabbix_log(LOG_LEVEL_DEBUG, "query [txnlev:%d] [%s]", txn_level, sql);
1185
1186 if (OCI_SUCCESS != (err = zbx_oracle_statement_prepare(sql)))
1187 ret = OCI_handle_sql_error(ERR_Z3005, err, sql);
1188
1189 if (ZBX_DB_FAIL == ret && 0 < txn_level)
1190 {
1191 zabbix_log(LOG_LEVEL_DEBUG, "query [%s] failed, setting transaction as failed", sql);
1192 txn_error = ZBX_DB_FAIL;
1193 }
1194
1195 return ret;
1196 }
1197
1198 /******************************************************************************
1199 * *
1200 * Function: db_bind_dynamic_cb *
1201 * *
1202 * Purpose: callback function used by dynamic parameter binding *
1203 * *
1204 ******************************************************************************/
db_bind_dynamic_cb(dvoid * ctxp,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 * alenp,ub1 * piecep,dvoid ** indpp)1205 static sb4 db_bind_dynamic_cb(dvoid *ctxp, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep,
1206 dvoid **indpp)
1207 {
1208 zbx_db_bind_context_t *context = (zbx_db_bind_context_t *)ctxp;
1209
1210 ZBX_UNUSED(bindp);
1211 ZBX_UNUSED(index);
1212
1213 switch (context->type)
1214 {
1215 case ZBX_TYPE_ID: /* handle 0 -> NULL conversion */
1216 if (0 == context->rows[iter][context->position].ui64)
1217 {
1218 *bufpp = NULL;
1219 *alenp = 0;
1220 break;
1221 }
1222 ZBX_FALLTHROUGH;
1223 case ZBX_TYPE_UINT:
1224 *bufpp = &((OCINumber *)context->data)[iter];
1225 *alenp = sizeof(OCINumber);
1226 break;
1227 case ZBX_TYPE_INT:
1228 *bufpp = &context->rows[iter][context->position].i32;
1229 *alenp = sizeof(int);
1230 break;
1231 case ZBX_TYPE_FLOAT:
1232 *bufpp = &context->rows[iter][context->position].dbl;
1233 *alenp = sizeof(double);
1234 break;
1235 case ZBX_TYPE_CHAR:
1236 case ZBX_TYPE_TEXT:
1237 case ZBX_TYPE_SHORTTEXT:
1238 case ZBX_TYPE_LONGTEXT:
1239 *bufpp = context->rows[iter][context->position].str;
1240 *alenp = ((size_t *)context->data)[iter];
1241 break;
1242 default:
1243 return FAIL;
1244 }
1245
1246 *indpp = NULL;
1247 *piecep = OCI_ONE_PIECE;
1248
1249 return OCI_CONTINUE;
1250 }
1251
1252 /******************************************************************************
1253 * *
1254 * Function: zbx_db_bind_parameter_dyn *
1255 * *
1256 * Purpose: performs dynamic parameter binding, converting value if necessary *
1257 * *
1258 * Parameters: context - [OUT] the bind context *
1259 * position - [IN] the parameter position *
1260 * type - [IN] the parameter type (ZBX_TYPE_* ) *
1261 * rows - [IN] the data to bind - array of rows, *
1262 * each row being an array of columns *
1263 * rows_num - [IN] the number of rows in the data *
1264 * *
1265 ******************************************************************************/
zbx_db_bind_parameter_dyn(zbx_db_bind_context_t * context,int position,unsigned char type,zbx_db_value_t ** rows,int rows_num)1266 int zbx_db_bind_parameter_dyn(zbx_db_bind_context_t *context, int position, unsigned char type,
1267 zbx_db_value_t **rows, int rows_num)
1268 {
1269 int i, ret = ZBX_DB_OK;
1270 size_t *sizes;
1271 sword err;
1272 OCINumber *values;
1273 ub2 data_type;
1274 OCIBind *bindhp = NULL;
1275
1276 context->position = position;
1277 context->rows = rows;
1278 context->data = NULL;
1279 context->type = type;
1280
1281 switch (type)
1282 {
1283 case ZBX_TYPE_ID:
1284 case ZBX_TYPE_UINT:
1285 values = (OCINumber *)zbx_malloc(NULL, sizeof(OCINumber) * rows_num);
1286
1287 for (i = 0; i < rows_num; i++)
1288 {
1289 err = OCINumberFromInt(oracle.errhp, &rows[i][position].ui64, sizeof(zbx_uint64_t),
1290 OCI_NUMBER_UNSIGNED, &values[i]);
1291
1292 if (OCI_SUCCESS != err)
1293 {
1294 ret = OCI_handle_sql_error(ERR_Z3007, err, NULL);
1295 goto out;
1296 }
1297 }
1298
1299 context->data = (OCINumber *)values;
1300 context->size_max = sizeof(OCINumber);
1301 data_type = SQLT_VNU;
1302 break;
1303 case ZBX_TYPE_INT:
1304 context->size_max = sizeof(int);
1305 data_type = SQLT_INT;
1306 break;
1307 case ZBX_TYPE_FLOAT:
1308 context->size_max = sizeof(double);
1309 data_type = SQLT_BDOUBLE;
1310 break;
1311 case ZBX_TYPE_CHAR:
1312 case ZBX_TYPE_TEXT:
1313 case ZBX_TYPE_SHORTTEXT:
1314 case ZBX_TYPE_LONGTEXT:
1315 sizes = (size_t *)zbx_malloc(NULL, sizeof(size_t) * rows_num);
1316 context->size_max = 0;
1317
1318 for (i = 0; i < rows_num; i++)
1319 {
1320 sizes[i] = strlen(rows[i][position].str);
1321
1322 if (sizes[i] > context->size_max)
1323 context->size_max = sizes[i];
1324 }
1325
1326 context->data = sizes;
1327 data_type = SQLT_LNG;
1328 break;
1329 default:
1330 THIS_SHOULD_NEVER_HAPPEN;
1331 exit(EXIT_FAILURE);
1332 }
1333
1334 err = OCIBindByPos(oracle.stmthp, &bindhp, oracle.errhp, context->position + 1, NULL, context->size_max,
1335 data_type, NULL, NULL, NULL, 0, NULL, (ub4)OCI_DATA_AT_EXEC);
1336
1337 if (OCI_SUCCESS != err)
1338 {
1339 ret = OCI_handle_sql_error(ERR_Z3007, err, NULL);
1340
1341 if (ZBX_DB_FAIL == ret && 0 < txn_level)
1342 {
1343 zabbix_log(LOG_LEVEL_DEBUG, "query failed, setting transaction as failed");
1344 txn_error = ZBX_DB_FAIL;
1345 }
1346
1347 goto out;
1348 }
1349
1350 err = OCIBindDynamic(bindhp, oracle.errhp, (dvoid *)context, db_bind_dynamic_cb, NULL, NULL);
1351
1352 if (OCI_SUCCESS != err)
1353 {
1354 ret = OCI_handle_sql_error(ERR_Z3007, err, NULL);
1355
1356 if (ZBX_DB_FAIL == ret && 0 < txn_level)
1357 {
1358 zabbix_log(LOG_LEVEL_DEBUG, "query failed, setting transaction as failed");
1359 txn_error = ZBX_DB_FAIL;
1360 }
1361
1362 goto out;
1363 }
1364 out:
1365 if (ret != ZBX_DB_OK)
1366 zbx_db_clean_bind_context(context);
1367
1368 return ret;
1369 }
1370
zbx_db_clean_bind_context(zbx_db_bind_context_t * context)1371 void zbx_db_clean_bind_context(zbx_db_bind_context_t *context)
1372 {
1373 zbx_free(context->data);
1374 }
1375
zbx_db_statement_execute(int iters)1376 int zbx_db_statement_execute(int iters)
1377 {
1378 sword err;
1379 ub4 nrows;
1380 int ret;
1381
1382 if (ZBX_DB_OK != txn_error)
1383 {
1384 zabbix_log(LOG_LEVEL_DEBUG, "ignoring query [txnlev:%d] within failed transaction", txn_level);
1385 ret = ZBX_DB_FAIL;
1386 goto out;
1387 }
1388
1389 if (OCI_SUCCESS != (err = zbx_oracle_statement_execute(iters, &nrows)))
1390 ret = OCI_handle_sql_error(ERR_Z3007, err, NULL);
1391 else
1392 ret = (int)nrows;
1393
1394 if (ZBX_DB_FAIL == ret && 0 < txn_level)
1395 {
1396 zabbix_log(LOG_LEVEL_DEBUG, "query failed, setting transaction as failed");
1397 txn_error = ZBX_DB_FAIL;
1398 }
1399 out:
1400 zabbix_log(LOG_LEVEL_DEBUG, "%s():%d", __func__, ret);
1401
1402 return ret;
1403 }
1404 #endif
1405
1406 /******************************************************************************
1407 * *
1408 * Function: zbx_db_vexecute *
1409 * *
1410 * Purpose: Execute SQL statement. For non-select statements only. *
1411 * *
1412 * Return value: ZBX_DB_FAIL (on error) or ZBX_DB_DOWN (on recoverable error) *
1413 * or number of rows affected (on success) *
1414 * *
1415 ******************************************************************************/
zbx_db_vexecute(const char * fmt,va_list args)1416 int zbx_db_vexecute(const char *fmt, va_list args)
1417 {
1418 char *sql = NULL;
1419 int ret = ZBX_DB_OK;
1420 double sec = 0;
1421 #if defined(HAVE_MYSQL)
1422 #elif defined(HAVE_ORACLE)
1423 sword err = OCI_SUCCESS;
1424 #elif defined(HAVE_POSTGRESQL)
1425 PGresult *result;
1426 char *error = NULL;
1427 #elif defined(HAVE_SQLITE3)
1428 int err;
1429 char *error = NULL;
1430 #endif
1431
1432 if (0 != CONFIG_LOG_SLOW_QUERIES)
1433 sec = zbx_time();
1434
1435 sql = zbx_dvsprintf(sql, fmt, args);
1436
1437 if (0 == txn_level)
1438 zabbix_log(LOG_LEVEL_DEBUG, "query without transaction detected");
1439
1440 if (ZBX_DB_OK != txn_error)
1441 {
1442 zabbix_log(LOG_LEVEL_DEBUG, "ignoring query [txnlev:%d] [%s] within failed transaction", txn_level,
1443 sql);
1444 ret = ZBX_DB_FAIL;
1445 goto clean;
1446 }
1447
1448 zabbix_log(LOG_LEVEL_DEBUG, "query [txnlev:%d] [%s]", txn_level, sql);
1449
1450 #if defined(HAVE_MYSQL)
1451 if (NULL == conn)
1452 {
1453 zbx_db_errlog(ERR_Z3003, 0, NULL, NULL);
1454 ret = ZBX_DB_FAIL;
1455 }
1456 else
1457 {
1458 if (0 != mysql_query(conn, sql))
1459 {
1460 zbx_db_errlog(ERR_Z3005, mysql_errno(conn), mysql_error(conn), sql);
1461
1462 ret = (SUCCEED == is_recoverable_mysql_error() ? ZBX_DB_DOWN : ZBX_DB_FAIL);
1463 }
1464 else
1465 {
1466 int status;
1467
1468 do
1469 {
1470 if (0 != mysql_field_count(conn))
1471 {
1472 zabbix_log(LOG_LEVEL_DEBUG, "cannot retrieve result set");
1473 break;
1474 }
1475
1476 ret += (int)mysql_affected_rows(conn);
1477
1478 /* more results? 0 = yes (keep looping), -1 = no, >0 = error */
1479 if (0 < (status = mysql_next_result(conn)))
1480 {
1481 zbx_db_errlog(ERR_Z3005, mysql_errno(conn), mysql_error(conn), sql);
1482 ret = (SUCCEED == is_recoverable_mysql_error() ? ZBX_DB_DOWN : ZBX_DB_FAIL);
1483 }
1484 }
1485 while (0 == status);
1486 }
1487 }
1488 #elif defined(HAVE_ORACLE)
1489 if (OCI_SUCCESS == (err = zbx_oracle_statement_prepare(sql)))
1490 {
1491 ub4 nrows = 0;
1492
1493 if (OCI_SUCCESS == (err = zbx_oracle_statement_execute(1, &nrows)))
1494 ret = (int)nrows;
1495 }
1496
1497 if (OCI_SUCCESS != err)
1498 ret = OCI_handle_sql_error(ERR_Z3005, err, sql);
1499
1500 #elif defined(HAVE_POSTGRESQL)
1501 result = PQexec(conn,sql);
1502
1503 if (NULL == result)
1504 {
1505 zbx_db_errlog(ERR_Z3005, 0, "result is NULL", sql);
1506 ret = (CONNECTION_OK == PQstatus(conn) ? ZBX_DB_FAIL : ZBX_DB_DOWN);
1507 }
1508 else if (PGRES_COMMAND_OK != PQresultStatus(result))
1509 {
1510 zbx_postgresql_error(&error, result);
1511 zbx_db_errlog(ERR_Z3005, 0, error, sql);
1512 zbx_free(error);
1513
1514 ret = (SUCCEED == is_recoverable_postgresql_error(conn, result) ? ZBX_DB_DOWN : ZBX_DB_FAIL);
1515 }
1516
1517 if (ZBX_DB_OK == ret)
1518 ret = atoi(PQcmdTuples(result));
1519
1520 PQclear(result);
1521 #elif defined(HAVE_SQLITE3)
1522 if (0 == txn_level)
1523 zbx_mutex_lock(sqlite_access);
1524
1525 lbl_exec:
1526 if (SQLITE_OK != (err = sqlite3_exec(conn, sql, NULL, 0, &error)))
1527 {
1528 if (SQLITE_BUSY == err)
1529 goto lbl_exec;
1530
1531 zbx_db_errlog(ERR_Z3005, 0, error, sql);
1532 sqlite3_free(error);
1533
1534 switch (err)
1535 {
1536 case SQLITE_ERROR: /* SQL error or missing database; assuming SQL error, because if we
1537 are this far into execution, zbx_db_connect() was successful */
1538 case SQLITE_NOMEM: /* A malloc() failed */
1539 case SQLITE_TOOBIG: /* String or BLOB exceeds size limit */
1540 case SQLITE_CONSTRAINT: /* Abort due to constraint violation */
1541 case SQLITE_MISMATCH: /* Data type mismatch */
1542 ret = ZBX_DB_FAIL;
1543 break;
1544 default:
1545 ret = ZBX_DB_DOWN;
1546 break;
1547 }
1548 }
1549
1550 if (ZBX_DB_OK == ret)
1551 ret = sqlite3_changes(conn);
1552
1553 if (0 == txn_level)
1554 zbx_mutex_unlock(sqlite_access);
1555 #endif /* HAVE_SQLITE3 */
1556
1557 if (0 != CONFIG_LOG_SLOW_QUERIES)
1558 {
1559 sec = zbx_time() - sec;
1560 if (sec > (double)CONFIG_LOG_SLOW_QUERIES / 1000.0)
1561 zabbix_log(LOG_LEVEL_WARNING, "slow query: " ZBX_FS_DBL " sec, \"%s\"", sec, sql);
1562 }
1563
1564 if (ZBX_DB_FAIL == ret && 0 < txn_level)
1565 {
1566 zabbix_log(LOG_LEVEL_DEBUG, "query [%s] failed, setting transaction as failed", sql);
1567 txn_error = ZBX_DB_FAIL;
1568 }
1569 clean:
1570 zbx_free(sql);
1571
1572 return ret;
1573 }
1574
1575 /******************************************************************************
1576 * *
1577 * Function: zbx_db_vselect *
1578 * *
1579 * Purpose: execute a select statement *
1580 * *
1581 * Return value: data, NULL (on error) or (DB_RESULT)ZBX_DB_DOWN *
1582 * *
1583 ******************************************************************************/
zbx_db_vselect(const char * fmt,va_list args)1584 DB_RESULT zbx_db_vselect(const char *fmt, va_list args)
1585 {
1586 char *sql = NULL;
1587 DB_RESULT result = NULL;
1588 double sec = 0;
1589 #if defined(HAVE_ORACLE)
1590 sword err = OCI_SUCCESS;
1591 ub4 prefetch_rows = 200, counter;
1592
1593 ZBX_UNUSED(counter);
1594 #elif defined(HAVE_POSTGRESQL)
1595 char *error = NULL;
1596 #elif defined(HAVE_SQLITE3)
1597 int ret = FAIL;
1598 char *error = NULL;
1599 #endif
1600
1601 if (0 != CONFIG_LOG_SLOW_QUERIES)
1602 sec = zbx_time();
1603
1604 sql = zbx_dvsprintf(sql, fmt, args);
1605
1606 if (ZBX_DB_OK != txn_error)
1607 {
1608 zabbix_log(LOG_LEVEL_DEBUG, "ignoring query [txnlev:%d] [%s] within failed transaction", txn_level, sql);
1609 goto clean;
1610 }
1611
1612 zabbix_log(LOG_LEVEL_DEBUG, "query [txnlev:%d] [%s]", txn_level, sql);
1613
1614 #if defined(HAVE_MYSQL)
1615 result = (DB_RESULT)zbx_malloc(NULL, sizeof(struct zbx_db_result));
1616 result->result = NULL;
1617
1618 if (NULL == conn)
1619 {
1620 zbx_db_errlog(ERR_Z3003, 0, NULL, NULL);
1621
1622 DBfree_result(result);
1623 result = NULL;
1624 }
1625 else
1626 {
1627 if (0 != mysql_query(conn, sql) || NULL == (result->result = mysql_store_result(conn)))
1628 {
1629 zbx_db_errlog(ERR_Z3005, mysql_errno(conn), mysql_error(conn), sql);
1630
1631 DBfree_result(result);
1632 result = (SUCCEED == is_recoverable_mysql_error() ? (DB_RESULT)ZBX_DB_DOWN : NULL);
1633 }
1634 }
1635 #elif defined(HAVE_ORACLE)
1636 result = zbx_malloc(NULL, sizeof(struct zbx_db_result));
1637 memset(result, 0, sizeof(struct zbx_db_result));
1638 zbx_vector_ptr_append(&oracle.db_results, result);
1639
1640 err = OCIHandleAlloc((dvoid *)oracle.envhp, (dvoid **)&result->stmthp, OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
1641
1642 /* Prefetching when working with Oracle is needed because otherwise it fetches only 1 row at a time when doing */
1643 /* selects (default behavior). There are 2 ways to do prefetching: memory based and rows based. Based on the */
1644 /* study optimal (speed-wise) memory based prefetch is 2 MB. But in case of many subsequent selects CPU usage */
1645 /* jumps up to 100 %. Using rows prefetch with up to 200 rows does not affect CPU usage, it is the same as */
1646 /* without prefetching at all. See ZBX-5920, ZBX-6493 for details. */
1647 /* */
1648 /* Tested on Oracle 11gR2. */
1649 /* */
1650 /* Oracle info: docs.oracle.com/cd/B28359_01/appdev.111/b28395/oci04sql.htm */
1651
1652 if (OCI_SUCCESS == err)
1653 {
1654 err = OCIAttrSet(result->stmthp, OCI_HTYPE_STMT, &prefetch_rows, sizeof(prefetch_rows),
1655 OCI_ATTR_PREFETCH_ROWS, oracle.errhp);
1656 }
1657
1658 if (OCI_SUCCESS == err)
1659 {
1660 err = OCIStmtPrepare(result->stmthp, oracle.errhp, (text *)sql, (ub4)strlen((char *)sql),
1661 (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT);
1662 }
1663
1664 if (OCI_SUCCESS == err)
1665 {
1666 err = OCIStmtExecute(oracle.svchp, result->stmthp, oracle.errhp, (ub4)0, (ub4)0,
1667 (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL,
1668 0 == txn_level ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
1669 }
1670
1671 if (OCI_SUCCESS == err)
1672 {
1673 /* get the number of columns in the query */
1674 err = OCIAttrGet((void *)result->stmthp, OCI_HTYPE_STMT, (void *)&result->ncolumn,
1675 (ub4 *)0, OCI_ATTR_PARAM_COUNT, oracle.errhp);
1676 }
1677
1678 if (OCI_SUCCESS != err)
1679 goto error;
1680
1681 assert(0 < result->ncolumn);
1682
1683 result->values = zbx_malloc(NULL, result->ncolumn * sizeof(char *));
1684 result->clobs = zbx_malloc(NULL, result->ncolumn * sizeof(OCILobLocator *));
1685 result->values_alloc = zbx_malloc(NULL, result->ncolumn * sizeof(ub4));
1686 memset(result->values, 0, result->ncolumn * sizeof(char *));
1687 memset(result->clobs, 0, result->ncolumn * sizeof(OCILobLocator *));
1688 memset(result->values_alloc, 0, result->ncolumn * sizeof(ub4));
1689
1690 for (counter = 1; OCI_SUCCESS == err && counter <= (ub4)result->ncolumn; counter++)
1691 {
1692 OCIParam *parmdp = NULL;
1693 OCIDefine *defnp = NULL;
1694 ub4 char_semantics;
1695 ub2 col_width = 0, data_type = 0;
1696
1697 /* request a parameter descriptor in the select-list */
1698 err = OCIParamGet((void *)result->stmthp, OCI_HTYPE_STMT, oracle.errhp, (void **)&parmdp, (ub4)counter);
1699
1700 if (OCI_SUCCESS == err)
1701 {
1702 /* retrieve the data type for the column */
1703 err = OCIAttrGet((void *)parmdp, OCI_DTYPE_PARAM, (dvoid *)&data_type, (ub4 *)NULL,
1704 (ub4)OCI_ATTR_DATA_TYPE, (OCIError *)oracle.errhp);
1705 }
1706
1707 if (SQLT_CLOB == data_type)
1708 {
1709 if (OCI_SUCCESS == err)
1710 {
1711 /* allocate the lob locator variable */
1712 err = OCIDescriptorAlloc((dvoid *)oracle.envhp, (dvoid **)&result->clobs[counter - 1],
1713 OCI_DTYPE_LOB, (size_t)0, (dvoid **)0);
1714 }
1715
1716 if (OCI_SUCCESS == err)
1717 {
1718 /* associate clob var with its define handle */
1719 err = OCIDefineByPos((void *)result->stmthp, &defnp, (OCIError *)oracle.errhp,
1720 (ub4)counter, (dvoid *)&result->clobs[counter - 1], (sb4)-1,
1721 data_type, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT);
1722 }
1723 }
1724 else
1725 {
1726 if (SQLT_IBDOUBLE != data_type && SQLT_BDOUBLE != data_type)
1727 {
1728 if (OCI_SUCCESS == err)
1729 {
1730 /* retrieve the length semantics for the column */
1731 char_semantics = 0;
1732 err = OCIAttrGet((void *)parmdp, (ub4)OCI_DTYPE_PARAM, (void *)&char_semantics,
1733 (ub4 *)NULL, (ub4)OCI_ATTR_CHAR_USED, (OCIError *)oracle.errhp);
1734 }
1735
1736 if (OCI_SUCCESS == err)
1737 {
1738 if (0 != char_semantics)
1739 {
1740 /* retrieve the column width in characters */
1741 err = OCIAttrGet((void *)parmdp, (ub4)OCI_DTYPE_PARAM, (void *)&col_width,
1742 (ub4 *)NULL, (ub4)OCI_ATTR_CHAR_SIZE, (OCIError *)oracle.errhp);
1743
1744 /* adjust for UTF-8 */
1745 col_width *= 4;
1746 }
1747 else
1748 {
1749 /* retrieve the column width in bytes */
1750 err = OCIAttrGet((void *)parmdp, (ub4)OCI_DTYPE_PARAM, (void *)&col_width,
1751 (ub4 *)NULL, (ub4)OCI_ATTR_DATA_SIZE, (OCIError *)oracle.errhp);
1752 }
1753 }
1754 col_width++; /* add 1 byte for terminating '\0' */
1755 }
1756 else
1757 col_width = ZBX_MAX_DOUBLE_LEN + 1;
1758
1759 result->values_alloc[counter - 1] = col_width;
1760 result->values[counter - 1] = zbx_malloc(NULL, col_width);
1761 *result->values[counter - 1] = '\0';
1762
1763 if (OCI_SUCCESS == err)
1764 {
1765 /* represent any data as characters */
1766 err = OCIDefineByPos(result->stmthp, &defnp, oracle.errhp, counter,
1767 (dvoid *)result->values[counter - 1], col_width, SQLT_STR,
1768 (dvoid *)0, (ub2 *)0, (ub2 *)0, OCI_DEFAULT);
1769 }
1770 }
1771
1772 /* free cell descriptor */
1773 OCIDescriptorFree(parmdp, OCI_DTYPE_PARAM);
1774 parmdp = NULL;
1775 }
1776 error:
1777 if (OCI_SUCCESS != err)
1778 {
1779 int server_status;
1780
1781 server_status = OCI_handle_sql_error(ERR_Z3005, err, sql);
1782 DBfree_result(result);
1783
1784 result = (ZBX_DB_DOWN == server_status ? (DB_RESULT)(intptr_t)server_status : NULL);
1785 }
1786 #elif defined(HAVE_POSTGRESQL)
1787 result = zbx_malloc(NULL, sizeof(struct zbx_db_result));
1788 result->pg_result = PQexec(conn, sql);
1789 result->values = NULL;
1790 result->cursor = 0;
1791 result->row_num = 0;
1792
1793 if (NULL == result->pg_result)
1794 zbx_db_errlog(ERR_Z3005, 0, "result is NULL", sql);
1795
1796 if (PGRES_TUPLES_OK != PQresultStatus(result->pg_result))
1797 {
1798 zbx_postgresql_error(&error, result->pg_result);
1799 zbx_db_errlog(ERR_Z3005, 0, error, sql);
1800 zbx_free(error);
1801
1802 if (SUCCEED == is_recoverable_postgresql_error(conn, result->pg_result))
1803 {
1804 DBfree_result(result);
1805 result = (DB_RESULT)ZBX_DB_DOWN;
1806 }
1807 else
1808 {
1809 DBfree_result(result);
1810 result = NULL;
1811 }
1812 }
1813 else /* init rownum */
1814 result->row_num = PQntuples(result->pg_result);
1815 #elif defined(HAVE_SQLITE3)
1816 if (0 == txn_level)
1817 zbx_mutex_lock(sqlite_access);
1818
1819 result = zbx_malloc(NULL, sizeof(struct zbx_db_result));
1820 result->curow = 0;
1821
1822 lbl_get_table:
1823 if (SQLITE_OK != (ret = sqlite3_get_table(conn,sql, &result->data, &result->nrow, &result->ncolumn, &error)))
1824 {
1825 if (SQLITE_BUSY == ret)
1826 goto lbl_get_table;
1827
1828 zbx_db_errlog(ERR_Z3005, 0, error, sql);
1829 sqlite3_free(error);
1830
1831 DBfree_result(result);
1832
1833 switch (ret)
1834 {
1835 case SQLITE_ERROR: /* SQL error or missing database; assuming SQL error, because if we
1836 are this far into execution, zbx_db_connect() was successful */
1837 case SQLITE_NOMEM: /* a malloc() failed */
1838 case SQLITE_MISMATCH: /* data type mismatch */
1839 result = NULL;
1840 break;
1841 default:
1842 result = (DB_RESULT)ZBX_DB_DOWN;
1843 break;
1844 }
1845 }
1846
1847 if (0 == txn_level)
1848 zbx_mutex_unlock(sqlite_access);
1849 #endif /* HAVE_SQLITE3 */
1850 if (0 != CONFIG_LOG_SLOW_QUERIES)
1851 {
1852 sec = zbx_time() - sec;
1853 if (sec > (double)CONFIG_LOG_SLOW_QUERIES / 1000.0)
1854 zabbix_log(LOG_LEVEL_WARNING, "slow query: " ZBX_FS_DBL " sec, \"%s\"", sec, sql);
1855 }
1856
1857 if (NULL == result && 0 < txn_level)
1858 {
1859 zabbix_log(LOG_LEVEL_DEBUG, "query [%s] failed, setting transaction as failed", sql);
1860 txn_error = ZBX_DB_FAIL;
1861 }
1862 clean:
1863 zbx_free(sql);
1864
1865 return result;
1866 }
1867
1868 /*
1869 * Execute SQL statement. For select statements only.
1870 */
zbx_db_select_n(const char * query,int n)1871 DB_RESULT zbx_db_select_n(const char *query, int n)
1872 {
1873 #if defined(HAVE_ORACLE)
1874 return zbx_db_select("select * from (%s) where rownum<=%d", query, n);
1875 #elif defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL) || defined(HAVE_SQLITE3)
1876 return zbx_db_select("%s limit %d", query, n);
1877 #endif
1878 }
1879
1880 #ifdef HAVE_POSTGRESQL
1881 /******************************************************************************
1882 * *
1883 * Function: zbx_db_bytea_unescape *
1884 * *
1885 * Purpose: converts the null terminated string into binary buffer *
1886 * *
1887 * Transformations: *
1888 * \ooo == a byte whose value = ooo (ooo is an octal number) *
1889 * \\ == \ *
1890 * *
1891 * Parameters: *
1892 * io - [IN/OUT] null terminated string / binary data *
1893 * *
1894 * Return value: length of the binary buffer *
1895 * *
1896 ******************************************************************************/
zbx_db_bytea_unescape(u_char * io)1897 static size_t zbx_db_bytea_unescape(u_char *io)
1898 {
1899 const u_char *i = io;
1900 u_char *o = io;
1901
1902 while ('\0' != *i)
1903 {
1904 switch (*i)
1905 {
1906 case '\\':
1907 i++;
1908 if ('\\' == *i)
1909 {
1910 *o++ = *i++;
1911 }
1912 else
1913 {
1914 if (0 != isdigit(i[0]) && 0 != isdigit(i[1]) && 0 != isdigit(i[2]))
1915 {
1916 *o = (*i++ - 0x30) << 6;
1917 *o += (*i++ - 0x30) << 3;
1918 *o++ += *i++ - 0x30;
1919 }
1920 }
1921 break;
1922
1923 default:
1924 *o++ = *i++;
1925 }
1926 }
1927
1928 return o - io;
1929 }
1930 #endif
1931
zbx_db_fetch(DB_RESULT result)1932 DB_ROW zbx_db_fetch(DB_RESULT result)
1933 {
1934 #if defined(HAVE_ORACLE)
1935 int i;
1936 sword rc;
1937 static char errbuf[512];
1938 sb4 errcode;
1939 #endif
1940
1941 if (NULL == result)
1942 return NULL;
1943
1944 #if defined(HAVE_MYSQL)
1945 if (NULL == result->result)
1946 return NULL;
1947
1948 return (DB_ROW)mysql_fetch_row(result->result);
1949 #elif defined(HAVE_ORACLE)
1950 if (NULL == result->stmthp)
1951 return NULL;
1952
1953 if (OCI_NO_DATA == (rc = OCIStmtFetch2(result->stmthp, oracle.errhp, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT)))
1954 return NULL;
1955
1956 if (OCI_SUCCESS != rc)
1957 {
1958 ub4 rows_fetched;
1959 ub4 sizep = sizeof(ub4);
1960
1961 if (OCI_SUCCESS != (rc = OCIErrorGet((dvoid *)oracle.errhp, (ub4)1, (text *)NULL,
1962 &errcode, (text *)errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR)))
1963 {
1964 zbx_db_errlog(ERR_Z3006, rc, zbx_oci_error(rc, NULL), NULL);
1965 return NULL;
1966 }
1967
1968 switch (errcode)
1969 {
1970 case 1012: /* ORA-01012: not logged on */
1971 case 2396: /* ORA-02396: exceeded maximum idle time */
1972 case 3113: /* ORA-03113: end-of-file on communication channel */
1973 case 3114: /* ORA-03114: not connected to ORACLE */
1974 zbx_db_errlog(ERR_Z3006, errcode, errbuf, NULL);
1975 return NULL;
1976 default:
1977 rc = OCIAttrGet((void *)result->stmthp, (ub4)OCI_HTYPE_STMT, (void *)&rows_fetched,
1978 (ub4 *)&sizep, (ub4)OCI_ATTR_ROWS_FETCHED, (OCIError *)oracle.errhp);
1979
1980 if (OCI_SUCCESS != rc || 1 != rows_fetched)
1981 {
1982 zbx_db_errlog(ERR_Z3006, errcode, errbuf, NULL);
1983 return NULL;
1984 }
1985 }
1986 }
1987
1988 for (i = 0; i < result->ncolumn; i++)
1989 {
1990 ub4 alloc, amount;
1991 ub1 csfrm;
1992 sword rc2;
1993
1994 if (NULL == result->clobs[i])
1995 continue;
1996
1997 if (OCI_SUCCESS != (rc2 = OCILobGetLength(oracle.svchp, oracle.errhp, result->clobs[i], &amount)))
1998 {
1999 /* If the LOB is NULL, the length is undefined. */
2000 /* In this case the function returns OCI_INVALID_HANDLE. */
2001 if (OCI_INVALID_HANDLE != rc2)
2002 {
2003 zbx_db_errlog(ERR_Z3006, rc2, zbx_oci_error(rc2, NULL), NULL);
2004 return NULL;
2005 }
2006 else
2007 amount = 0;
2008 }
2009 else if (OCI_SUCCESS != (rc2 = OCILobCharSetForm(oracle.envhp, oracle.errhp, result->clobs[i], &csfrm)))
2010 {
2011 zbx_db_errlog(ERR_Z3006, rc2, zbx_oci_error(rc2, NULL), NULL);
2012 return NULL;
2013 }
2014
2015 if (result->values_alloc[i] < (alloc = amount * ZBX_MAX_BYTES_IN_UTF8_CHAR + 1))
2016 {
2017 result->values_alloc[i] = alloc;
2018 result->values[i] = zbx_realloc(result->values[i], result->values_alloc[i]);
2019 }
2020
2021 if (OCI_SUCCESS == rc2)
2022 {
2023 if (OCI_SUCCESS != (rc2 = OCILobRead(oracle.svchp, oracle.errhp, result->clobs[i], &amount,
2024 (ub4)1, (dvoid *)result->values[i], (ub4)(result->values_alloc[i] - 1),
2025 (dvoid *)NULL, (OCICallbackLobRead)NULL, (ub2)0, csfrm)))
2026 {
2027 zbx_db_errlog(ERR_Z3006, rc2, zbx_oci_error(rc2, NULL), NULL);
2028 return NULL;
2029 }
2030 }
2031
2032 result->values[i][amount] = '\0';
2033 }
2034
2035 return result->values;
2036 #elif defined(HAVE_POSTGRESQL)
2037 /* free old data */
2038 if (NULL != result->values)
2039 zbx_free(result->values);
2040
2041 /* EOF */
2042 if (result->cursor == result->row_num)
2043 return NULL;
2044
2045 /* init result */
2046 result->fld_num = PQnfields(result->pg_result);
2047
2048 if (result->fld_num > 0)
2049 {
2050 int i;
2051
2052 result->values = zbx_malloc(result->values, sizeof(char *) * result->fld_num);
2053
2054 for (i = 0; i < result->fld_num; i++)
2055 {
2056 if (PQgetisnull(result->pg_result, result->cursor, i))
2057 {
2058 result->values[i] = NULL;
2059 }
2060 else
2061 {
2062 result->values[i] = PQgetvalue(result->pg_result, result->cursor, i);
2063 if (PQftype(result->pg_result, i) == ZBX_PG_BYTEAOID) /* binary data type BYTEAOID */
2064 zbx_db_bytea_unescape((u_char *)result->values[i]);
2065 }
2066 }
2067 }
2068
2069 result->cursor++;
2070
2071 return result->values;
2072 #elif defined(HAVE_SQLITE3)
2073 /* EOF */
2074 if (result->curow >= result->nrow)
2075 return NULL;
2076
2077 if (NULL == result->data)
2078 return NULL;
2079
2080 result->curow++; /* NOTE: first row == header row */
2081
2082 return &(result->data[result->curow * result->ncolumn]);
2083 #endif
2084 }
2085
zbx_db_is_null(const char * field)2086 int zbx_db_is_null(const char *field)
2087 {
2088 if (NULL == field)
2089 return SUCCEED;
2090 #ifdef HAVE_ORACLE
2091 if ('\0' == *field)
2092 return SUCCEED;
2093 #endif
2094 return FAIL;
2095 }
2096
2097 #ifdef HAVE_ORACLE
OCI_DBclean_result(DB_RESULT result)2098 static void OCI_DBclean_result(DB_RESULT result)
2099 {
2100 if (NULL == result)
2101 return;
2102
2103 if (NULL != result->values)
2104 {
2105 int i;
2106
2107 for (i = 0; i < result->ncolumn; i++)
2108 {
2109 zbx_free(result->values[i]);
2110
2111 /* deallocate the lob locator variable */
2112 if (NULL != result->clobs[i])
2113 {
2114 OCIDescriptorFree((dvoid *)result->clobs[i], OCI_DTYPE_LOB);
2115 result->clobs[i] = NULL;
2116 }
2117 }
2118
2119 zbx_free(result->values);
2120 zbx_free(result->clobs);
2121 zbx_free(result->values_alloc);
2122 }
2123
2124 if (result->stmthp)
2125 {
2126 OCIHandleFree((dvoid *)result->stmthp, OCI_HTYPE_STMT);
2127 result->stmthp = NULL;
2128 }
2129 }
2130 #endif
2131
DBfree_result(DB_RESULT result)2132 void DBfree_result(DB_RESULT result)
2133 {
2134 #if defined(HAVE_MYSQL)
2135 if (NULL == result)
2136 return;
2137
2138 mysql_free_result(result->result);
2139 zbx_free(result);
2140 #elif defined(HAVE_ORACLE)
2141 int i;
2142
2143 if (NULL == result)
2144 return;
2145
2146 OCI_DBclean_result(result);
2147
2148 for (i = 0; i < oracle.db_results.values_num; i++)
2149 {
2150 if (oracle.db_results.values[i] == result)
2151 {
2152 zbx_vector_ptr_remove_noorder(&oracle.db_results, i);
2153 break;
2154 }
2155 }
2156
2157 zbx_free(result);
2158 #elif defined(HAVE_POSTGRESQL)
2159 if (NULL == result)
2160 return;
2161
2162 if (NULL != result->values)
2163 {
2164 result->fld_num = 0;
2165 zbx_free(result->values);
2166 result->values = NULL;
2167 }
2168
2169 PQclear(result->pg_result);
2170 zbx_free(result);
2171 #elif defined(HAVE_SQLITE3)
2172 if (NULL == result)
2173 return;
2174
2175 if (NULL != result->data)
2176 {
2177 sqlite3_free_table(result->data);
2178 }
2179
2180 zbx_free(result);
2181 #endif /* HAVE_SQLITE3 */
2182 }
2183
2184 #ifdef HAVE_ORACLE
2185 /* server status: OCI_SERVER_NORMAL or OCI_SERVER_NOT_CONNECTED */
OCI_DBserver_status(void)2186 static ub4 OCI_DBserver_status(void)
2187 {
2188 sword err;
2189 ub4 server_status = OCI_SERVER_NOT_CONNECTED;
2190
2191 err = OCIAttrGet((void *)oracle.srvhp, OCI_HTYPE_SERVER, (void *)&server_status,
2192 (ub4 *)0, OCI_ATTR_SERVER_STATUS, (OCIError *)oracle.errhp);
2193
2194 if (OCI_SUCCESS != err)
2195 zabbix_log(LOG_LEVEL_WARNING, "cannot determine Oracle server status, assuming not connected");
2196
2197 return server_status;
2198 }
2199 #endif /* HAVE_ORACLE */
2200
zbx_db_is_escape_sequence(char c)2201 static int zbx_db_is_escape_sequence(char c)
2202 {
2203 #if defined(HAVE_MYSQL)
2204 if ('\'' == c || '\\' == c)
2205 #elif defined(HAVE_POSTGRESQL)
2206 if ('\'' == c || ('\\' == c && 1 == ZBX_PG_ESCAPE_BACKSLASH))
2207 #else
2208 if ('\'' == c)
2209 #endif
2210 return SUCCEED;
2211
2212 return FAIL;
2213 }
2214
2215 /******************************************************************************
2216 * *
2217 * Function: zbx_db_escape_string *
2218 * *
2219 * Return value: escaped string *
2220 * *
2221 * Comments: sync changes with 'zbx_db_get_escape_string_len' *
2222 * and 'zbx_db_dyn_escape_string' *
2223 * *
2224 ******************************************************************************/
zbx_db_escape_string(const char * src,char * dst,size_t len,zbx_escape_sequence_t flag)2225 static void zbx_db_escape_string(const char *src, char *dst, size_t len, zbx_escape_sequence_t flag)
2226 {
2227 const char *s;
2228 char *d;
2229
2230 assert(dst);
2231
2232 len--; /* '\0' */
2233
2234 for (s = src, d = dst; NULL != s && '\0' != *s && 0 < len; s++)
2235 {
2236 if (ESCAPE_SEQUENCE_ON == flag && SUCCEED == zbx_db_is_escape_sequence(*s))
2237 {
2238 if (2 > len)
2239 break;
2240
2241 #if defined(HAVE_MYSQL)
2242 *d++ = '\\';
2243 #elif defined(HAVE_POSTGRESQL)
2244 *d++ = *s;
2245 #else
2246 *d++ = '\'';
2247 #endif
2248 len--;
2249 }
2250 *d++ = *s;
2251 len--;
2252 }
2253 *d = '\0';
2254 }
2255
2256 /******************************************************************************
2257 * *
2258 * Function: zbx_db_get_escape_string_len *
2259 * *
2260 * Purpose: to calculate escaped string length limited by bytes or characters *
2261 * whichever is reached first. *
2262 * *
2263 * Parameters: s - [IN] string to escape *
2264 * max_bytes - [IN] limit in bytes *
2265 * max_chars - [IN] limit in characters *
2266 * flag - [IN] sequences need to be escaped on/off *
2267 * *
2268 * Return value: return length in bytes of escaped string *
2269 * with terminating '\0' *
2270 * *
2271 ******************************************************************************/
zbx_db_get_escape_string_len(const char * s,size_t max_bytes,size_t max_chars,zbx_escape_sequence_t flag)2272 static size_t zbx_db_get_escape_string_len(const char *s, size_t max_bytes, size_t max_chars,
2273 zbx_escape_sequence_t flag)
2274 {
2275 size_t csize, len = 1; /* '\0' */
2276
2277 if (NULL == s)
2278 return len;
2279
2280 while ('\0' != *s && 0 < max_chars)
2281 {
2282 csize = zbx_utf8_char_len(s);
2283
2284 /* process non-UTF-8 characters as single byte characters */
2285 if (0 == csize)
2286 csize = 1;
2287
2288 if (max_bytes < csize)
2289 break;
2290
2291 if (ESCAPE_SEQUENCE_ON == flag && SUCCEED == zbx_db_is_escape_sequence(*s))
2292 len++;
2293
2294 s += csize;
2295 len += csize;
2296 max_bytes -= csize;
2297 max_chars--;
2298 }
2299
2300 return len;
2301 }
2302
2303 /******************************************************************************
2304 * *
2305 * Function: zbx_db_dyn_escape_string *
2306 * *
2307 * Purpose: to escape string limited by bytes or characters, whichever limit *
2308 * is reached first. *
2309 * *
2310 * Parameters: src - [IN] string to escape *
2311 * max_bytes - [IN] limit in bytes *
2312 * max_chars - [IN] limit in characters *
2313 * flag - [IN] sequences need to be escaped on/off *
2314 * *
2315 * Return value: escaped string *
2316 * *
2317 ******************************************************************************/
zbx_db_dyn_escape_string(const char * src,size_t max_bytes,size_t max_chars,zbx_escape_sequence_t flag)2318 char *zbx_db_dyn_escape_string(const char *src, size_t max_bytes, size_t max_chars, zbx_escape_sequence_t flag)
2319 {
2320 char *dst = NULL;
2321 size_t len;
2322
2323 len = zbx_db_get_escape_string_len(src, max_bytes, max_chars, flag);
2324
2325 dst = (char *)zbx_malloc(dst, len);
2326
2327 zbx_db_escape_string(src, dst, len, flag);
2328
2329 return dst;
2330 }
2331
2332 /******************************************************************************
2333 * *
2334 * Function: zbx_db_get_escape_like_pattern_len *
2335 * *
2336 * Return value: return length of escaped LIKE pattern with terminating '\0' *
2337 * *
2338 * Comments: sync changes with 'zbx_db_escape_like_pattern' *
2339 * *
2340 ******************************************************************************/
zbx_db_get_escape_like_pattern_len(const char * src)2341 static int zbx_db_get_escape_like_pattern_len(const char *src)
2342 {
2343 int len;
2344 const char *s;
2345
2346 len = zbx_db_get_escape_string_len(src, ZBX_SIZE_T_MAX, ZBX_SIZE_T_MAX, ESCAPE_SEQUENCE_ON) - 1; /* minus '\0' */
2347
2348 for (s = src; s && *s; s++)
2349 {
2350 len += (*s == '_' || *s == '%' || *s == ZBX_SQL_LIKE_ESCAPE_CHAR);
2351 len += 1;
2352 }
2353
2354 len++; /* '\0' */
2355
2356 return len;
2357 }
2358
2359 /******************************************************************************
2360 * *
2361 * Function: zbx_db_escape_like_pattern *
2362 * *
2363 * Return value: escaped string to be used as pattern in LIKE *
2364 * *
2365 * Comments: sync changes with 'zbx_db_get_escape_like_pattern_len' *
2366 * *
2367 * For instance, we wish to find string a_b%c\d'e!f in our database *
2368 * using '!' as escape character. Our queries then become: *
2369 * *
2370 * ... LIKE 'a!_b!%c\\d\'e!!f' ESCAPE '!' (MySQL, PostgreSQL) *
2371 * ... LIKE 'a!_b!%c\d''e!!f' ESCAPE '!' (Oracle, SQLite3) *
2372 * *
2373 * Using backslash as escape character in LIKE would be too much *
2374 * trouble, because escaping backslashes would have to be escaped *
2375 * as well, like so: *
2376 * *
2377 * ... LIKE 'a\\_b\\%c\\\\d\'e!f' ESCAPE '\\' or *
2378 * ... LIKE 'a\\_b\\%c\\\\d\\\'e!f' ESCAPE '\\' (MySQL, PostgreSQL) *
2379 * ... LIKE 'a\_b\%c\\d''e!f' ESCAPE '\' (Oracle, SQLite3) *
2380 * *
2381 * Hence '!' instead of backslash. *
2382 * *
2383 ******************************************************************************/
zbx_db_escape_like_pattern(const char * src,char * dst,int len)2384 static void zbx_db_escape_like_pattern(const char *src, char *dst, int len)
2385 {
2386 char *d;
2387 char *tmp = NULL;
2388 const char *t;
2389
2390 assert(dst);
2391
2392 tmp = (char *)zbx_malloc(tmp, len);
2393
2394 zbx_db_escape_string(src, tmp, len, ESCAPE_SEQUENCE_ON);
2395
2396 len--; /* '\0' */
2397
2398 for (t = tmp, d = dst; t && *t && len; t++)
2399 {
2400 if (*t == '_' || *t == '%' || *t == ZBX_SQL_LIKE_ESCAPE_CHAR)
2401 {
2402 if (len <= 1)
2403 break;
2404 *d++ = ZBX_SQL_LIKE_ESCAPE_CHAR;
2405 len--;
2406 }
2407 *d++ = *t;
2408 len--;
2409 }
2410
2411 *d = '\0';
2412
2413 zbx_free(tmp);
2414 }
2415
2416 /******************************************************************************
2417 * *
2418 * Function: zbx_db_dyn_escape_like_pattern *
2419 * *
2420 * Return value: escaped string to be used as pattern in LIKE *
2421 * *
2422 ******************************************************************************/
zbx_db_dyn_escape_like_pattern(const char * src)2423 char *zbx_db_dyn_escape_like_pattern(const char *src)
2424 {
2425 int len;
2426 char *dst = NULL;
2427
2428 len = zbx_db_get_escape_like_pattern_len(src);
2429
2430 dst = (char *)zbx_malloc(dst, len);
2431
2432 zbx_db_escape_like_pattern(src, dst, len);
2433
2434 return dst;
2435 }
2436
2437 /******************************************************************************
2438 * *
2439 * Function: zbx_db_strlen_n *
2440 * *
2441 * Purpose: return the string length to fit into a database field of the *
2442 * specified size *
2443 * *
2444 * Return value: the string length in bytes *
2445 * *
2446 ******************************************************************************/
zbx_db_strlen_n(const char * text_loc,size_t maxlen)2447 int zbx_db_strlen_n(const char *text_loc, size_t maxlen)
2448 {
2449 return zbx_strlen_utf8_nchars(text_loc, maxlen);
2450 }
2451
2452 /******************************************************************************
2453 * *
2454 * Function: zbx_db_version_check *
2455 * *
2456 * Purpose: determine if a vendor database(MySQL, MariaDB, PostgreSQL, *
2457 * Oracle, ElasticDB) version satisfies Zabbix requirements *
2458 * *
2459 * Parameters: database - [IN] database name *
2460 * current_version - [IN] detected numeric version *
2461 * min_version - [IN] minimum required numeric version *
2462 * max_version - [IN] maximum required numeric version *
2463 * *
2464 * Return value: resulting status flag *
2465 * *
2466 ******************************************************************************/
zbx_db_version_check(const char * database,zbx_uint32_t current_version,zbx_uint32_t min_version,zbx_uint32_t max_version)2467 int zbx_db_version_check(const char *database, zbx_uint32_t current_version, zbx_uint32_t min_version,
2468 zbx_uint32_t max_version)
2469 {
2470 int flag;
2471
2472 if (ZBX_DBVERSION_UNDEFINED == current_version)
2473 {
2474 flag = DB_VERSION_FAILED_TO_RETRIEVE;
2475 zabbix_log(LOG_LEVEL_WARNING, "Failed to retrieve %s version", database);
2476 }
2477 else if (min_version > current_version && ZBX_DBVERSION_UNDEFINED != min_version)
2478 {
2479 flag = DB_VERSION_LOWER_THAN_MINIMUM;
2480 zabbix_log(LOG_LEVEL_WARNING, "Unsupported DB! %s version is %lu which is smaller than minimum of %lu",
2481 database, (unsigned long)current_version, (unsigned long)min_version);
2482 }
2483 else if (max_version < current_version && ZBX_DBVERSION_UNDEFINED != max_version)
2484 {
2485 flag = DB_VERSION_HIGHER_THAN_MAXIMUM;
2486 zabbix_log(LOG_LEVEL_WARNING, "Unsupported DB! %s version is %lu which is higher than maximum of %lu",
2487 database, (unsigned long)current_version, (unsigned long)max_version);
2488 }
2489 else
2490 flag = DB_VERSION_SUPPORTED;
2491
2492 return flag;
2493 }
2494
2495 /******************************************************************************
2496 * *
2497 * Function: zbx_db_version_json_create *
2498 * *
2499 * Purpose: prepare json for front-end with the DB current, minimum and *
2500 * maximum versions and a flag that indicates if the version *
2501 * satisfies the requirements *
2502 * *
2503 * Parameters: json - [IN/OUT] json data *
2504 * database - [IN] name of DB (MySQL/ElasticDB) *
2505 * friendly_current_version - [IN] string current version *
2506 * friendly_min_version - [IN] string min version *
2507 * friendly_max_version - [IN] string max version *
2508 * flag - [IN] status if DB satisfies the *
2509 * requirements *
2510 * *
2511 ******************************************************************************/
zbx_db_version_json_create(struct zbx_json * json,const char * database,const char * friendly_current_version,const char * friendly_min_version,const char * friendly_max_version,int flag)2512 void zbx_db_version_json_create(struct zbx_json *json, const char *database, const char *friendly_current_version,
2513 const char *friendly_min_version, const char *friendly_max_version, int flag)
2514 {
2515 zbx_json_addobject(json, NULL);
2516 zbx_json_addstring(json, "database", database, ZBX_JSON_TYPE_STRING);
2517
2518 if (DB_VERSION_FAILED_TO_RETRIEVE != flag)
2519 zbx_json_addstring(json, "current_version", friendly_current_version, ZBX_JSON_TYPE_STRING);
2520
2521 zbx_json_addstring(json, "min_version", friendly_min_version, ZBX_JSON_TYPE_STRING);
2522 zbx_json_addstring(json, "max_version", friendly_max_version, ZBX_JSON_TYPE_STRING);
2523 zbx_json_addint64(json, "flag", flag);
2524 zbx_json_close(json);
2525 }
2526
2527 /******************************************************************************
2528 * *
2529 * Function: zbx_dbms_version_get *
2530 * *
2531 * Purpose: For PostgreSQL, MySQL and MariaDB: *
2532 * returns DBMS version as integer: MMmmuu *
2533 * M = major version part *
2534 * m = minor version part *
2535 * u = patch version part *
2536 * *
2537 * Example: if the original DB version was 1.2.34 then 10234 gets returned *
2538 * *
2539 * Purpose: For OracleDB: *
2540 * returns DBMS version as integer: MRruRRivUU *
2541 * MR = major release version part *
2542 * ru = release update version part *
2543 * RR = release update version revision part *
2544 * iv = increment version part *
2545 * UU = unused, reserved for future use *
2546 * *
2547 * Example: if the OracleDB version was 18.1.0.0.7 then 1801000007 gets *
2548 * returned *
2549 * *
2550 * Return value: DBMS version or DBVERSION_UNDEFINED if unknown *
2551 * *
2552 ******************************************************************************/
zbx_dbms_version_get(void)2553 zbx_uint32_t zbx_dbms_version_get(void)
2554 {
2555 #if defined(HAVE_MYSQL)
2556 return ZBX_MYSQL_SVERSION;
2557 #elif defined(HAVE_POSTGRESQL)
2558 return ZBX_PG_SVERSION;
2559 #elif defined(HAVE_ORACLE)
2560 return ZBX_ORACLE_SVERSION;
2561 #else
2562 return ZBX_DBVERSION_UNDEFINED;
2563 #endif
2564 }
2565
2566 #ifdef HAVE_MYSQL
2567 /******************************************************************************
2568 * *
2569 * Function: zbx_dbms_mariadb_used *
2570 * *
2571 * Purpose: returns flag if the mariadb was detected *
2572 * *
2573 * Return value: ON - mariadb detected *
2574 * OFF - otherwise (it is unforked mysql) *
2575 ******************************************************************************/
zbx_dbms_mariadb_used(void)2576 int zbx_dbms_mariadb_used(void)
2577 {
2578 return ZBX_MARIADB_SFORK;
2579 }
2580 #endif
2581
2582 /***************************************************************************************************************
2583 * *
2584 * Function: zbx_dbms_version_extract *
2585 * *
2586 * Purpose: retrieves the DB version and makes sure it is stored in the numeric format, also fills the json *
2587 * to report to front-end *
2588 * *
2589 * For PostgreSQL: *
2590 * numeric version is available from the API *
2591 * *
2592 * For MySQL and MariaDB: *
2593 * numeric version is available from the API, but also the additional processing is required *
2594 * to determine if it is a MySQL or MariaDB and save this result as well *
2595 * *
2596 * For Oracle: *
2597 * numeric version needs to be manually parsed from the string result *
2598 * Oracle DB format is like 18.1.2.3.0 where *
2599 * 18 - major release version *
2600 * 1 - release update version *
2601 * 2 - release update version revision *
2602 * 3 - increment version *
2603 * 0 - unused, reserved for future use *
2604 * *
2605 * Oracle Examples: *
2606 * For "Oracle Database 18c Express Edition Release 1.0.0.0.0 - Production" => 100000000 *
2607 * For "Oracle Database 18c Express Edition Release 18.2.0.0.7 - Production" => 1802000007 *
2608 * For "Oracle Database 18c Express Edition Release 0.0.34.123.7 - Production" => DBVERSION_UNDEFINED *
2609 * For "Oracle Database 18c Express Edition Release 1.0.3.x.7 - Production" => DBVERISON_UNDEFINED *
2610 * For "<anything else>" => DBVERSION_UNDEFINED *
2611 * *
2612 **************************************************************************************************************/
zbx_dbms_version_extract(struct zbx_json * json)2613 zbx_uint32_t zbx_dbms_version_extract(struct zbx_json *json)
2614 {
2615 #define RIGHT2(x) ((int)((zbx_uint32_t)(x) - ((zbx_uint32_t)((x)/100))*100))
2616 #if defined(HAVE_MYSQL)
2617 int flag, client_major_version, client_minor_version, client_release_version, server_major_version,
2618 server_minor_version, server_release_version;
2619 const char *info;
2620 char *version_friendly;
2621
2622 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2623
2624 if (NULL != (info = mysql_get_server_info(conn)) && NULL != strstr(info, "MariaDB"))
2625 {
2626 zabbix_log(LOG_LEVEL_DEBUG, "MariaDB fork detected");
2627 ZBX_MARIADB_SFORK = ON;
2628 }
2629
2630 if (ON == ZBX_MARIADB_SFORK && 6 == sscanf(info, "%d.%d.%d-%d.%d.%d-MariaDB", &client_major_version,
2631 &client_minor_version, &client_release_version, &server_major_version,
2632 &server_minor_version, &server_release_version))
2633 {
2634 ZBX_MYSQL_SVERSION = server_major_version * 10000 + server_minor_version * 100 +
2635 server_release_version;
2636 zabbix_log(LOG_LEVEL_DEBUG, "MariaDB subversion detected");
2637 }
2638 else
2639 ZBX_MYSQL_SVERSION = (zbx_uint32_t)mysql_get_server_version(conn);
2640
2641 version_friendly = zbx_dsprintf(NULL, "%d.%.2d.%.2d", RIGHT2(ZBX_MYSQL_SVERSION/10000),
2642 RIGHT2(ZBX_MYSQL_SVERSION/100), RIGHT2(ZBX_MYSQL_SVERSION));
2643
2644 if (ON == ZBX_MARIADB_SFORK)
2645 {
2646 flag = zbx_db_version_check("MariaDB", ZBX_MYSQL_SVERSION, ZBX_MARIA_MIN_VERSION, ZBX_DBVERSION_UNDEFINED);
2647 zbx_db_version_json_create(json, "MariaDB", version_friendly,
2648 ZBX_MARIA_MIN_VERSION_FRIENDLY, ZBX_MARIA_MAX_VERSION_FRIENDLY, flag);
2649 }
2650 else
2651 {
2652 flag = zbx_db_version_check("MySQL", ZBX_MYSQL_SVERSION, ZBX_MYSQL_MIN_VERSION, ZBX_MYSQL_MAX_VERSION);
2653 zbx_db_version_json_create(json, "MySQL", version_friendly,
2654 ZBX_MYSQL_MIN_VERSION_FRIENDLY, ZBX_MYSQL_MAX_VERSION_FRIENDLY, flag);
2655 }
2656
2657 zbx_free(version_friendly);
2658 #elif defined(HAVE_POSTGRESQL)
2659 int flag;
2660 char *version_friendly;
2661 zbx_uint32_t major;
2662
2663 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2664 ZBX_PG_SVERSION = PQserverVersion(conn);
2665 major = RIGHT2(ZBX_PG_SVERSION/10000);
2666
2667 if (10 > major)
2668 {
2669 version_friendly = zbx_dsprintf(NULL, "%d.%d.%d", major, RIGHT2(ZBX_PG_SVERSION/100),
2670 RIGHT2(ZBX_PG_SVERSION));
2671 }
2672 else
2673 {
2674 version_friendly = zbx_dsprintf(NULL, "%d.%d", major, RIGHT2(ZBX_PG_SVERSION));
2675 }
2676
2677 flag = zbx_db_version_check("PostgreSQL", ZBX_PG_SVERSION, ZBX_POSTGRESQL_MIN_VERSION,
2678 ZBX_POSTGRESQL_MAX_VERSION);
2679 zbx_db_version_json_create(json, "PostgreSQL", version_friendly,
2680 ZBX_POSTGRESQL_MIN_VERSION_FRIENDLY, ZBX_POSTGRESQL_MAX_VERSION_FRIENDLY, flag);
2681 zbx_free(version_friendly);
2682 #elif defined(HAVE_ORACLE)
2683 # ifdef HAVE_OCI_SERVER_RELEASE2
2684 char *version_str = "Version ";
2685 ub4 oci_ver = 0;
2686 # endif
2687 char *start, *release_str = "Release ";
2688 char version_friendly[MAX_STRING_LEN / 8];
2689 int flag, major_release_version, release_update_version, release_update_version_revision,
2690 increment_version, reserved_for_future_use, overall_status = SUCCEED;
2691
2692 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2693 # ifdef HAVE_OCI_SERVER_RELEASE2
2694 if (OCI_SUCCESS != OCIServerRelease2(oracle.svchp, oracle.errhp, (OraText *) version_friendly,
2695 (ub4)sizeof(version_friendly), OCI_HTYPE_SVCCTX, &oci_ver, OCI_DEFAULT))
2696 # else
2697 if (OCI_SUCCESS != OCIServerVersion(oracle.svchp, oracle.errhp, (OraText *) version_friendly,
2698 (ub4)sizeof(version_friendly), OCI_HTYPE_SVCCTX))
2699 # endif
2700 {
2701 overall_status = FAIL;
2702 goto out;
2703 }
2704
2705 zabbix_log(LOG_LEVEL_DEBUG, "OracleDB version retrieved unparsed: %s", version_friendly);
2706
2707 if (
2708 # ifdef HAVE_OCI_SERVER_RELEASE2
2709 NULL != (start = strstr(version_friendly, version_str)) ||
2710 # endif
2711 NULL != (start = strstr(version_friendly, release_str)))
2712 {
2713 size_t next_start_index;
2714
2715 next_start_index = start - version_friendly + strlen(release_str); /* same length for version_str */
2716
2717 if (5 != sscanf(version_friendly + next_start_index, "%d.%d.%d.%d.%d", &major_release_version,
2718 &release_update_version, &release_update_version_revision, &increment_version,
2719 &reserved_for_future_use) || major_release_version >= 100 ||
2720 major_release_version <= 0 || release_update_version >= 100 ||
2721 release_update_version < 0 || release_update_version_revision >= 100 ||
2722 release_update_version_revision < 0 || increment_version >= 100 ||
2723 increment_version < 0)
2724 {
2725 zabbix_log(LOG_LEVEL_WARNING, "Unexpected Oracle DB version format: %s", version_friendly);
2726 overall_status = FAIL;
2727 }
2728 }
2729 else
2730 {
2731 zabbix_log(LOG_LEVEL_WARNING, "Cannot find Release keyword in Oracle DB version.");
2732 overall_status = FAIL;
2733 }
2734 out:
2735 if (FAIL == overall_status)
2736 {
2737 zabbix_log(LOG_LEVEL_WARNING, "Failed to detect OracleDB version");
2738 ZBX_ORACLE_SVERSION = ZBX_DBVERSION_UNDEFINED;
2739 }
2740 else
2741 {
2742 ZBX_ORACLE_SVERSION = major_release_version * 100000000 + release_update_version * 1000000 +
2743 release_update_version_revision * 10000 + increment_version * 100 +
2744 reserved_for_future_use;
2745 # ifndef HAVE_OCI_SERVER_RELEASE2
2746 if (18 <= major_release_version)
2747 {
2748 zabbix_log(LOG_LEVEL_WARNING, "Unable to determine the accurate Oracle DB version "
2749 "(possibly there is a DB driver - DB version mismatch, "
2750 "only the major Oracle DB version can be established): %s", version_friendly);
2751 }
2752 # endif
2753 }
2754
2755 flag = zbx_db_version_check("Oracle", ZBX_ORACLE_SVERSION, ZBX_ORACLE_MIN_VERSION, ZBX_ORACLE_MAX_VERSION);
2756 zbx_db_version_json_create(json, "Oracle", version_friendly, ZBX_ORACLE_MIN_VERSION_FRIENDLY,
2757 ZBX_ORACLE_MAX_VERSION_FRIENDLY, flag);
2758 #else
2759 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2760 #endif
2761 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() version:%lu", __func__, (unsigned long)zbx_dbms_version_get());
2762
2763 return zbx_dbms_version_get();
2764 }
2765
2766 #if defined(HAVE_POSTGRESQL)
2767 /******************************************************************************
2768 * *
2769 * Function: zbx_tsdb_get_version *
2770 * *
2771 * Purpose: returns TimescaleDB (TSDB) version as integer: MMmmuu *
2772 * M = major version part *
2773 * m = minor version part *
2774 * u = patch version part *
2775 * *
2776 * Example: TSDB 1.5.1 version will be returned as 10501 *
2777 * *
2778 * Return value: TSDB version or 0 if unknown or the extension not installed *
2779 * *
2780 ******************************************************************************/
zbx_tsdb_get_version(void)2781 int zbx_tsdb_get_version(void)
2782 {
2783 int ver, major, minor, patch;
2784 DB_RESULT result;
2785 DB_ROW row;
2786
2787 if (-1 == ZBX_TSDB_VERSION)
2788 {
2789 /* catalog pg_extension not available */
2790 if (90001 > ZBX_PG_SVERSION)
2791 {
2792 ver = ZBX_TSDB_VERSION = 0;
2793 goto out;
2794 }
2795
2796 result = zbx_db_select("select extversion from pg_extension where extname = 'timescaledb'");
2797
2798 /* database down, can re-query in the next call */
2799 if ((DB_RESULT)ZBX_DB_DOWN == result)
2800 {
2801 ver = 0;
2802 goto out;
2803 }
2804
2805 /* extension is not installed */
2806 if (NULL == result)
2807 {
2808 ver = ZBX_TSDB_VERSION = 0;
2809 goto out;
2810 }
2811
2812 if (NULL != (row = zbx_db_fetch(result)) &&
2813 3 == sscanf((const char*)row[0], "%d.%d.%d", &major, &minor, &patch))
2814 {
2815 ver = major * 10000;
2816 ver += minor * 100;
2817 ver += patch;
2818 ZBX_TSDB_VERSION = ver;
2819 }
2820 else
2821 ver = ZBX_TSDB_VERSION = 0;
2822
2823 DBfree_result(result);
2824 }
2825 else
2826 ver = ZBX_TSDB_VERSION;
2827 out:
2828 return ver;
2829 }
2830
2831 #endif
2832