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 #ifdef HAVE_UNIXODBC
23
24 #include <sql.h>
25 #include <sqlext.h>
26 #include <sqltypes.h>
27
28 #include "odbc.h"
29 #include "log.h"
30 #include "zbxjson.h"
31 #include "zbxalgo.h"
32
33 struct zbx_odbc_data_source
34 {
35 SQLHENV henv;
36 SQLHDBC hdbc;
37 };
38
39 struct zbx_odbc_query_result
40 {
41 SQLHSTMT hstmt;
42 SQLSMALLINT col_num;
43 char **row;
44 };
45
46 #define ZBX_FLAG_ODBC_NONE 0x00
47 #define ZBX_FLAG_ODBC_LLD 0x01
48
49 /******************************************************************************
50 * *
51 * Function: zbx_odbc_rc_str *
52 * *
53 * Purpose: get human readable representation of ODBC return code *
54 * *
55 * Parameters: rc - [IN] ODBC return code *
56 * *
57 * Return value: human readable representation of error code or NULL if the *
58 * given code is unknown *
59 * *
60 ******************************************************************************/
zbx_odbc_rc_str(SQLRETURN rc)61 static const char *zbx_odbc_rc_str(SQLRETURN rc)
62 {
63 switch (rc)
64 {
65 case SQL_ERROR:
66 return "SQL_ERROR";
67 case SQL_SUCCESS_WITH_INFO:
68 return "SQL_SUCCESS_WITH_INFO";
69 case SQL_NO_DATA:
70 return "SQL_NO_DATA";
71 case SQL_INVALID_HANDLE:
72 return "SQL_INVALID_HANDLE";
73 case SQL_STILL_EXECUTING:
74 return "SQL_STILL_EXECUTING";
75 case SQL_NEED_DATA:
76 return "SQL_NEED_DATA";
77 case SQL_SUCCESS:
78 return "SQL_SUCCESS";
79 default:
80 return NULL;
81 }
82 }
83
84 /******************************************************************************
85 * *
86 * Function: zbx_odbc_diag *
87 * *
88 * Purpose: diagnose result of ODBC function call *
89 * *
90 * Parameters: h_type - [IN] type of handle call was executed on *
91 * h - [IN] handle call was executed on *
92 * rc - [IN] function return code *
93 * diag - [OUT] diagnostic message *
94 * *
95 * Return value: SUCCEED - function call was successful *
96 * FAIL - otherwise, error message is returned in diag *
97 * *
98 * Comments: It is caller's responsibility to free diag in case this function *
99 * returns FAIL! *
100 * *
101 ******************************************************************************/
zbx_odbc_diag(SQLSMALLINT h_type,SQLHANDLE h,SQLRETURN rc,char ** diag)102 static int zbx_odbc_diag(SQLSMALLINT h_type, SQLHANDLE h, SQLRETURN rc, char **diag)
103 {
104 const char *rc_str = NULL;
105 char *buffer = NULL;
106 size_t alloc = 0, offset = 0;
107
108 if (SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
109 {
110 SQLCHAR sql_state[SQL_SQLSTATE_SIZE + 1], err_msg[128];
111 SQLINTEGER err_code = 0;
112 SQLSMALLINT rec_nr = 1;
113
114 while (0 != SQL_SUCCEEDED(SQLGetDiagRec(h_type, h, rec_nr++, sql_state, &err_code, err_msg,
115 sizeof(err_msg), NULL)))
116 {
117 zbx_chrcpy_alloc(&buffer, &alloc, &offset, (NULL == buffer ? ':' : '|'));
118 zbx_snprintf_alloc(&buffer, &alloc, &offset, "[%s][%ld][%s]", sql_state, (long)err_code, err_msg);
119 }
120 }
121
122 if (0 != SQL_SUCCEEDED(rc))
123 {
124 if (NULL == (rc_str = zbx_odbc_rc_str(rc)))
125 {
126 zabbix_log(LOG_LEVEL_TRACE, "%s(): [%d (unknown SQLRETURN code)]%s", __func__,
127 (int)rc, ZBX_NULL2EMPTY_STR(buffer));
128 }
129 else
130 zabbix_log(LOG_LEVEL_TRACE, "%s(): [%s]%s", __func__, rc_str, ZBX_NULL2EMPTY_STR(buffer));
131 }
132 else
133 {
134 if (NULL == (rc_str = zbx_odbc_rc_str(rc)))
135 {
136 *diag = zbx_dsprintf(*diag, "[%d (unknown SQLRETURN code)]%s",
137 (int)rc, ZBX_NULL2EMPTY_STR(buffer));
138 }
139 else
140 *diag = zbx_dsprintf(*diag, "[%s]%s", rc_str, ZBX_NULL2EMPTY_STR(buffer));
141
142 zabbix_log(LOG_LEVEL_TRACE, "%s(): %s", __func__, *diag);
143 }
144
145 zbx_free(buffer);
146
147 return 0 != SQL_SUCCEEDED(rc) ? SUCCEED : FAIL;
148 }
149
150 /******************************************************************************
151 * *
152 * Function: zbx_log_odbc_connection_info *
153 * *
154 * Purpose: log details upon successful connection on behalf of caller *
155 * *
156 * Parameters: function - [IN] caller function name *
157 * hdbc - [IN] ODBC connection handle *
158 * *
159 ******************************************************************************/
zbx_log_odbc_connection_info(const char * function,SQLHDBC hdbc)160 static void zbx_log_odbc_connection_info(const char *function, SQLHDBC hdbc)
161 {
162 if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
163 {
164 char driver_name[MAX_STRING_LEN + 1], driver_ver[MAX_STRING_LEN + 1],
165 db_name[MAX_STRING_LEN + 1], db_ver[MAX_STRING_LEN + 1], *diag = NULL;
166 SQLRETURN rc;
167
168 rc = SQLGetInfo(hdbc, SQL_DRIVER_NAME, driver_name, MAX_STRING_LEN, NULL);
169
170 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_DBC, hdbc, rc, &diag))
171 {
172 zabbix_log(LOG_LEVEL_DEBUG, "Cannot obtain driver name: %s", diag);
173 zbx_strlcpy(driver_name, "unknown", sizeof(driver_name));
174 }
175
176 rc = SQLGetInfo(hdbc, SQL_DRIVER_VER, driver_ver, MAX_STRING_LEN, NULL);
177
178 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_DBC, hdbc, rc, &diag))
179 {
180 zabbix_log(LOG_LEVEL_DEBUG, "Cannot obtain driver version: %s", diag);
181 zbx_strlcpy(driver_ver, "unknown", sizeof(driver_ver));
182 }
183
184 rc = SQLGetInfo(hdbc, SQL_DBMS_NAME, db_name, MAX_STRING_LEN, NULL);
185
186 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_DBC, hdbc, rc, &diag))
187 {
188 zabbix_log(LOG_LEVEL_DEBUG, "Cannot obtain database name: %s", diag);
189 zbx_strlcpy(db_name, "unknown", sizeof(db_name));
190 }
191
192 rc = SQLGetInfo(hdbc, SQL_DBMS_VER, db_ver, MAX_STRING_LEN, NULL);
193
194 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_DBC, hdbc, rc, &diag))
195 {
196 zabbix_log(LOG_LEVEL_DEBUG, "Cannot obtain database version: %s", diag);
197 zbx_strlcpy(db_ver, "unknown", sizeof(db_ver));
198 }
199
200 zabbix_log(LOG_LEVEL_DEBUG, "%s() connected to %s(%s) using %s(%s)", function,
201 db_name, db_ver, driver_name, driver_ver);
202 zbx_free(diag);
203 }
204 }
205
206 /******************************************************************************
207 * *
208 * Function: zbx_odbc_connection_string_append *
209 * *
210 * Purpose: Appends a new argument to ODBC connection string. *
211 * Connection string is reallocated to fit new value. *
212 * *
213 * Parameters: connection_str - [IN/OUT] connection string *
214 * attribute - [IN] attribute name *
215 * value - [IN] attribute value *
216 * *
217 ******************************************************************************/
zbx_odbc_connection_string_append(char ** connection_str,const char * attribute,const char * value)218 static void zbx_odbc_connection_string_append(char **connection_str, const char *attribute, const char *value)
219 {
220 size_t len;
221 char last = '\0';
222
223 if (NULL == value)
224 return;
225
226 if (0 < (len = strlen(*connection_str)))
227 last = (*connection_str)[len-1];
228
229 *connection_str = zbx_dsprintf(*connection_str, "%s%s%s=%s", *connection_str, ';' == last ? "" : ";",
230 attribute, value);
231 }
232
233 /******************************************************************************
234 * *
235 * Function: zbx_odbc_connect *
236 * *
237 * Purpose: connect to ODBC data source *
238 * *
239 * Parameters: dsn - [IN] data source name *
240 * connection - [IN] connection string *
241 * user - [IN] user name *
242 * pass - [IN] password *
243 * timeout - [IN] timeout *
244 * error - [OUT] error message *
245 * *
246 * Return value: pointer to opaque data source data structure or NULL in case *
247 * of failure, allocated error message is returned in error *
248 * *
249 * Comments: It is caller's responsibility to free error buffer! *
250 * *
251 ******************************************************************************/
zbx_odbc_connect(const char * dsn,const char * connection,const char * user,const char * pass,int timeout,char ** error)252 zbx_odbc_data_source_t *zbx_odbc_connect(const char *dsn, const char *connection, const char *user, const char *pass,
253 int timeout, char **error)
254 {
255 char *diag = NULL;
256 zbx_odbc_data_source_t *data_source = NULL;
257 SQLRETURN rc;
258
259 zabbix_log(LOG_LEVEL_DEBUG, "In %s() dsn:'%s' user:'%s'", __func__, dsn, user);
260
261 data_source = (zbx_odbc_data_source_t *)zbx_malloc(data_source, sizeof(zbx_odbc_data_source_t));
262
263 if (0 != SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &data_source->henv)))
264 {
265 rc = SQLSetEnvAttr(data_source->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
266
267 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_ENV, data_source->henv, rc, &diag))
268 {
269 rc = SQLAllocHandle(SQL_HANDLE_DBC, data_source->henv, &data_source->hdbc);
270
271 if(SUCCEED == zbx_odbc_diag(SQL_HANDLE_ENV, data_source->henv, rc, &diag))
272 {
273 rc = SQLSetConnectAttr(data_source->hdbc, (SQLINTEGER)SQL_LOGIN_TIMEOUT,
274 (SQLPOINTER)(intptr_t)timeout, (SQLINTEGER)0);
275
276 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_DBC, data_source->hdbc, rc, &diag))
277 {
278 /* look for user in data source instead of no user */
279 if ('\0' == *user)
280 user = NULL;
281
282 /* look for password in data source instead of no password */
283 if ('\0' == *pass)
284 pass = NULL;
285
286 if (NULL != connection && '\0' != *connection)
287 {
288 char *connection_str;
289
290 connection_str = NULL;
291
292 if (NULL != user || NULL != pass)
293 {
294 connection_str = zbx_strdup(NULL, connection);
295 zbx_odbc_connection_string_append(&connection_str, "UID", user);
296 zbx_odbc_connection_string_append(&connection_str, "PWD", pass);
297 connection = connection_str;
298 }
299
300 rc = SQLDriverConnect(data_source->hdbc, NULL,
301 (SQLCHAR *)connection, SQL_NTS, NULL, 0, NULL,
302 SQL_DRIVER_NOPROMPT);
303
304 zbx_free(connection_str);
305 }
306 else
307 {
308 rc = SQLConnect(data_source->hdbc, (SQLCHAR *)dsn, SQL_NTS,
309 (SQLCHAR *)user, SQL_NTS, (SQLCHAR *)pass, SQL_NTS);
310 }
311
312 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_DBC, data_source->hdbc, rc, &diag))
313 {
314 zbx_log_odbc_connection_info(__func__, data_source->hdbc);
315 goto out;
316 }
317
318 *error = zbx_dsprintf(*error, "Cannot connect to ODBC DSN: %s", diag);
319 }
320 else
321 *error = zbx_dsprintf(*error, "Cannot set ODBC login timeout: %s", diag);
322
323 SQLFreeHandle(SQL_HANDLE_DBC, data_source->hdbc);
324 }
325 else
326 *error = zbx_dsprintf(*error, "Cannot create ODBC connection handle: %s", diag);
327 }
328 else
329 *error = zbx_dsprintf(*error, "Cannot set ODBC version: %s", diag);
330
331 SQLFreeHandle(SQL_HANDLE_ENV, data_source->henv);
332 }
333 else
334 *error = zbx_strdup(*error, "Cannot create ODBC environment handle.");
335
336 zbx_free(data_source);
337 out:
338 zbx_free(diag);
339
340 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
341
342 return data_source;
343 }
344
345 /******************************************************************************
346 * *
347 * Function: zbx_odbc_data_source_free *
348 * *
349 * Purpose: free resources allocated by successful zbx_odbc_connect() call *
350 * *
351 * Parameters: data_source - [IN] pointer to data source structure *
352 * *
353 * Comments: Input parameter data_source must be obtained using *
354 * zbx_odbc_connect() and must not be NULL. *
355 * *
356 ******************************************************************************/
zbx_odbc_data_source_free(zbx_odbc_data_source_t * data_source)357 void zbx_odbc_data_source_free(zbx_odbc_data_source_t *data_source)
358 {
359 SQLDisconnect(data_source->hdbc);
360 SQLFreeHandle(SQL_HANDLE_DBC, data_source->hdbc);
361 SQLFreeHandle(SQL_HANDLE_ENV, data_source->henv);
362 zbx_free(data_source);
363 }
364
365 /******************************************************************************
366 * *
367 * Function: zbx_odbc_select *
368 * *
369 * Purpose: execute a query to ODBC data source *
370 * *
371 * Parameters: data_source - [IN] pointer to data source structure *
372 * query - [IN] SQL query *
373 * error - [OUT] error message *
374 * *
375 * Return value: pointer to opaque query result structure or NULL in case of *
376 * failure, allocated error message is returned in error *
377 * *
378 * Comments: It is caller's responsibility to free error buffer! *
379 * *
380 ******************************************************************************/
zbx_odbc_select(const zbx_odbc_data_source_t * data_source,const char * query,char ** error)381 zbx_odbc_query_result_t *zbx_odbc_select(const zbx_odbc_data_source_t *data_source, const char *query, char **error)
382 {
383 char *diag = NULL;
384 zbx_odbc_query_result_t *query_result = NULL;
385 SQLRETURN rc;
386
387 zabbix_log(LOG_LEVEL_DEBUG, "In %s() query:'%s'", __func__, query);
388
389 if (NULL == query || '\0' == *query)
390 {
391 *error = zbx_strdup(*error, "SQL query cannot be empty.");
392 goto out;
393 }
394
395 query_result = (zbx_odbc_query_result_t *)zbx_malloc(query_result, sizeof(zbx_odbc_query_result_t));
396
397 rc = SQLAllocHandle(SQL_HANDLE_STMT, data_source->hdbc, &query_result->hstmt);
398
399 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_DBC, data_source->hdbc, rc, &diag))
400 {
401 rc = SQLExecDirect(query_result->hstmt, (SQLCHAR *)query, SQL_NTS);
402
403 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_STMT, query_result->hstmt, rc, &diag))
404 {
405 rc = SQLNumResultCols(query_result->hstmt, &query_result->col_num);
406
407 if (SUCCEED == zbx_odbc_diag(SQL_HANDLE_STMT, query_result->hstmt, rc, &diag))
408 {
409 SQLSMALLINT i;
410
411 query_result->row = (char **)zbx_malloc(NULL, sizeof(char *) * (size_t)query_result->col_num);
412
413 for (i = 0; ; i++)
414 {
415 if (i == query_result->col_num)
416 {
417 zabbix_log(LOG_LEVEL_DEBUG, "selected all %d columns",
418 (int)query_result->col_num);
419 goto out;
420 }
421
422 query_result->row[i] = NULL;
423 }
424 }
425 else
426 *error = zbx_dsprintf(*error, "Cannot get number of columns in ODBC result: %s", diag);
427 }
428 else
429 *error = zbx_dsprintf(*error, "Cannot execute ODBC query: %s", diag);
430
431 SQLFreeHandle(SQL_HANDLE_STMT, query_result->hstmt);
432 }
433 else
434 *error = zbx_dsprintf(*error, "Cannot create ODBC statement handle: %s", diag);
435
436 zbx_free(query_result);
437 out:
438 zbx_free(diag);
439
440 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
441
442 return query_result;
443 }
444
445 /******************************************************************************
446 * *
447 * Function: zbx_odbc_query_result_free *
448 * *
449 * Purpose: free resources allocated by successful zbx_odbc_select() call *
450 * *
451 * Parameters: query_result - [IN] pointer to query result structure *
452 * *
453 * Comments: Input parameter query_result must be obtained using *
454 * zbx_odbc_select() and must not be NULL. *
455 * *
456 ******************************************************************************/
zbx_odbc_query_result_free(zbx_odbc_query_result_t * query_result)457 void zbx_odbc_query_result_free(zbx_odbc_query_result_t *query_result)
458 {
459 SQLSMALLINT i;
460
461 SQLFreeHandle(SQL_HANDLE_STMT, query_result->hstmt);
462
463 for (i = 0; i < query_result->col_num; i++)
464 zbx_free(query_result->row[i]);
465
466 zbx_free(query_result->row);
467 zbx_free(query_result);
468 }
469
470 /******************************************************************************
471 * *
472 * Function: zbx_odbc_fetch *
473 * *
474 * Purpose: fetch single row of ODBC query result *
475 * *
476 * Parameters: query_result - [IN] pointer to query result structure *
477 * *
478 * Return value: array of strings or NULL (see Comments) *
479 * *
480 * Comments: NULL result can signify both end of rows (which is normal) and *
481 * failure. There is currently no way to distinguish these cases. *
482 * There is no need to free strings returned by this function. *
483 * Lifetime of strings is limited to next call of zbx_odbc_fetch() *
484 * or zbx_odbc_query_result_free(), caller needs to make a copy if *
485 * result is needed for longer. *
486 * *
487 ******************************************************************************/
zbx_odbc_fetch(zbx_odbc_query_result_t * query_result)488 static const char *const *zbx_odbc_fetch(zbx_odbc_query_result_t *query_result)
489 {
490 char *diag = NULL;
491 SQLRETURN rc;
492 SQLSMALLINT i;
493 const char *const *row = NULL;
494
495 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
496
497 if (SQL_NO_DATA == (rc = SQLFetch(query_result->hstmt)))
498 {
499 /* end of rows */
500 goto out;
501 }
502
503 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_STMT, query_result->hstmt, rc, &diag))
504 {
505 zabbix_log(LOG_LEVEL_DEBUG, "Cannot fetch row: %s", diag);
506 goto out;
507 }
508
509 for (i = 0; i < query_result->col_num; i++)
510 {
511 size_t alloc = 0, offset = 0;
512 char buffer[MAX_STRING_LEN + 1];
513 SQLLEN len;
514
515 zbx_free(query_result->row[i]);
516
517 /* force len to integer value for DB2 compatibility */
518 do
519 {
520 rc = SQLGetData(query_result->hstmt, i + 1, SQL_C_CHAR, buffer, MAX_STRING_LEN, &len);
521
522 if (SUCCEED != zbx_odbc_diag(SQL_HANDLE_STMT, query_result->hstmt, rc, &diag))
523 {
524 zabbix_log(LOG_LEVEL_DEBUG, "Cannot get column data: %s", diag);
525 goto out;
526 }
527
528 if (SQL_NULL_DATA == (int)len)
529 break;
530
531 zbx_strcpy_alloc(&query_result->row[i], &alloc, &offset, buffer);
532 }
533 while (SQL_SUCCESS != rc);
534
535 if (NULL != query_result->row[i])
536 zbx_rtrim(query_result->row[i], " ");
537
538 zabbix_log(LOG_LEVEL_DEBUG, "column #%d value:'%s'", (int)i + 1, ZBX_NULL2STR(query_result->row[i]));
539 }
540
541 row = (const char *const *)query_result->row;
542 out:
543 zbx_free(diag);
544
545 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
546
547 return row;
548 }
549
550 /******************************************************************************
551 * *
552 * Function: zbx_odbc_query_result_to_string *
553 * *
554 * Purpose: extract the first column of the first row of ODBC SQL query *
555 * *
556 * Parameters: query_result - [IN] result of SQL query *
557 * string - [OUT] the first column of the first row *
558 * error - [OUT] error message *
559 * *
560 * Return value: SUCCEED - result wasn't empty, the first column of the first *
561 * result row is not NULL and is returned in string *
562 * parameter, error remains untouched in this case *
563 * FAIL - otherwise, allocated error message is returned in *
564 * error parameter, string remains untouched *
565 * *
566 * Comments: It is caller's responsibility to free allocated buffers! *
567 * *
568 ******************************************************************************/
zbx_odbc_query_result_to_string(zbx_odbc_query_result_t * query_result,char ** string,char ** error)569 int zbx_odbc_query_result_to_string(zbx_odbc_query_result_t *query_result, char **string, char **error)
570 {
571 const char *const *row;
572 int ret = FAIL;
573
574 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
575
576 if (NULL != (row = zbx_odbc_fetch(query_result)))
577 {
578 if (NULL != row[0])
579 {
580 *string = zbx_strdup(*string, row[0]);
581 zbx_replace_invalid_utf8(*string);
582 ret = SUCCEED;
583 }
584 else
585 *error = zbx_strdup(*error, "SQL query returned NULL value.");
586 }
587 else
588 *error = zbx_strdup(*error, "SQL query returned empty result.");
589
590 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
591
592 return ret;
593 }
594
595 /******************************************************************************
596 * *
597 * Function: odbc_query_result_to_json *
598 * *
599 * Purpose: convert ODBC SQL query result into JSON *
600 * *
601 * Parameters: query_result - [IN] result of SQL query *
602 * flags - [IN] specify if column names must be converted *
603 * to LLD macros or preserved as they are *
604 * out_json - [OUT] query result converted to JSON *
605 * error - [OUT] error message *
606 * *
607 * Return value: SUCCEED - conversion was successful and allocated LLD JSON *
608 * is returned in lld_json parameter, error remains *
609 * untouched in this case *
610 * FAIL - otherwise, allocated error message is returned in *
611 * error parameter, lld_json remains untouched *
612 * *
613 * Comments: It is caller's responsibility to free allocated buffers! *
614 * *
615 ******************************************************************************/
odbc_query_result_to_json(zbx_odbc_query_result_t * query_result,int flags,char ** out_json,char ** error)616 static int odbc_query_result_to_json(zbx_odbc_query_result_t *query_result, int flags, char **out_json,
617 char **error)
618 {
619 const char *const *row;
620 struct zbx_json json;
621 zbx_vector_str_t names;
622 int ret = FAIL, i, j;
623
624 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
625
626 zbx_vector_str_create(&names);
627 zbx_vector_str_reserve(&names, query_result->col_num);
628
629 for (i = 0; i < query_result->col_num; i++)
630 {
631 char str[MAX_STRING_LEN], *p;
632 SQLRETURN rc;
633 SQLSMALLINT len;
634
635 rc = SQLColAttribute(query_result->hstmt, i + 1, SQL_DESC_LABEL, str, sizeof(str), &len, NULL);
636
637 if (SQL_SUCCESS != rc || sizeof(str) <= (size_t)len || '\0' == *str)
638 {
639 *error = zbx_dsprintf(*error, "Cannot obtain column #%d name.", i + 1);
640 goto out;
641 }
642
643 zabbix_log(LOG_LEVEL_DEBUG, "column #%d name:'%s'", i + 1, str);
644
645 if (flags & ZBX_FLAG_ODBC_LLD)
646 {
647 for (p = str; '\0' != *p; p++)
648 {
649 if (0 != isalpha((unsigned char)*p))
650 *p = toupper((unsigned char)*p);
651
652 if (SUCCEED != is_macro_char(*p))
653 {
654 *error = zbx_dsprintf(*error, "Cannot convert column #%d name to macro.", i + 1);
655 goto out;
656 }
657 }
658
659 zbx_vector_str_append(&names, zbx_dsprintf(NULL, "{#%s}", str));
660
661 for (j = 0; j < i; j++)
662 {
663 if (0 == strcmp(names.values[i], names.values[j]))
664 {
665 *error = zbx_dsprintf(*error, "Duplicate macro name: %s.", names.values[i]);
666 goto out;
667 }
668 }
669 }
670 else
671 {
672 char *name;
673
674 zbx_replace_invalid_utf8((name = zbx_strdup(NULL, str)));
675 zbx_vector_str_append(&names, name);
676 }
677 }
678
679 zbx_json_initarray(&json, ZBX_JSON_STAT_BUF_LEN);
680
681 while (NULL != (row = zbx_odbc_fetch(query_result)))
682 {
683 zbx_json_addobject(&json, NULL);
684
685 for (i = 0; i < query_result->col_num; i++)
686 {
687 char *value = NULL;
688
689 if (NULL != row[i])
690 {
691 value = zbx_strdup(value, row[i]);
692 zbx_replace_invalid_utf8(value);
693 }
694
695 zbx_json_addstring(&json, names.values[i], value, ZBX_JSON_TYPE_STRING);
696 zbx_free(value);
697 }
698
699 zbx_json_close(&json);
700 }
701
702 zbx_json_close(&json);
703
704 *out_json = zbx_strdup(*out_json, json.buffer);
705
706 zbx_json_free(&json);
707
708 ret = SUCCEED;
709 out:
710 zbx_vector_str_clear_ext(&names, zbx_str_free);
711 zbx_vector_str_destroy(&names);
712
713 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
714
715 return ret;
716 }
717
718 /******************************************************************************
719 * *
720 * Function: zbx_odbc_query_result_to_lld_json *
721 * *
722 * Purpose: public wrapper for odbc_query_result_to_json *
723 * *
724 *****************************************************************************/
zbx_odbc_query_result_to_lld_json(zbx_odbc_query_result_t * query_result,char ** lld_json,char ** error)725 int zbx_odbc_query_result_to_lld_json(zbx_odbc_query_result_t *query_result, char **lld_json, char **error)
726 {
727 return odbc_query_result_to_json(query_result, ZBX_FLAG_ODBC_LLD, lld_json, error);
728 }
729
730 /******************************************************************************
731 * *
732 * Function: zbx_odbc_query_result_to_json *
733 * *
734 * Purpose: public wrapper for odbc_query_result_to_json *
735 * *
736 *****************************************************************************/
zbx_odbc_query_result_to_json(zbx_odbc_query_result_t * query_result,char ** out_json,char ** error)737 int zbx_odbc_query_result_to_json(zbx_odbc_query_result_t *query_result, char **out_json, char **error)
738 {
739 return odbc_query_result_to_json(query_result, ZBX_FLAG_ODBC_NONE, out_json, error);
740 }
741
742 #endif /* HAVE_UNIXODBC */
743