1 /*
2 * ProFTPD: mod_sql_odbc -- Support for connecting to databases via ODBC
3 * Copyright (c) 2003-2020 TJ Saunders
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, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders gives permission to link this program
20 * with OpenSSL, and distribute the resulting executable, without including
21 * the source code for OpenSSL in the source distribution.
22 */
23
24 #include "conf.h"
25 #include "mod_sql.h"
26
27 #define MOD_SQL_ODBC_VERSION "mod_sql_odbc/0.3.4"
28
29 /* Make sure the version of proftpd is as necessary. */
30 #if PROFTPD_VERSION_NUMBER < 0x0001030602
31 # error "ProFTPD 1.3.6rc2 or later required"
32 #endif
33
34 #include "sql.h"
35 #include "sqlext.h"
36
37 module sql_odbc_module;
38
39 typedef struct db_conn_struct {
40 char *dsn;
41 char *user;
42 char *pass;
43
44 HENV envh;
45 HDBC dbh;
46 HSTMT sth;
47
48 unsigned int state;
49
50 } db_conn_t;
51
52 #define SQLODBC_HAVE_ENV_HANDLE 0x0001
53 #define SQLODBC_HAVE_DBC_HANDLE 0x0002
54 #define SQLODBC_HAVE_STMT_HANDLE 0x0004
55 #define SQLODBC_HAVE_INFO 0x0010
56
57 typedef struct conn_entry_struct {
58 char *name;
59 void *data;
60
61 /* Timer handling */
62 int timer;
63 int ttl;
64
65 /* Connection handling */
66 unsigned int nconn;
67
68 } conn_entry_t;
69
70 #define DEF_CONN_POOL_SIZE 10
71
72 static pool *conn_pool = NULL;
73 static array_header *conn_cache = NULL;
74 static int odbc_version = SQL_OV_ODBC3;
75 static const char *odbc_version_str = "ODBCv3";
76
77 /* Default to using the LIMIT clause. Some database drivers prefer
78 * ROWNUM (e.g. Oracle), and others prefer TOP (e.g. TDS).
79 */
80 static int use_limit_clause = TRUE;
81 static int use_rownum_clause = FALSE;
82 static int use_top_clause = FALSE;
83
84 MODRET sqlodbc_close(cmd_rec *);
85
sqlodbc_get_conn(char * name)86 static conn_entry_t *sqlodbc_get_conn(char *name) {
87 register unsigned int i = 0;
88
89 if (!name)
90 return NULL;
91
92 for (i = 0; i < conn_cache->nelts; i++) {
93 conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
94
95 if (strcmp(name, entry->name) == 0)
96 return entry;
97 }
98
99 return NULL;
100 }
101
sqlodbc_add_conn(pool * p,char * name,db_conn_t * conn)102 static void *sqlodbc_add_conn(pool *p, char *name, db_conn_t *conn) {
103 conn_entry_t *entry = NULL;
104
105 if (p == NULL ||
106 name == NULL ||
107 conn == NULL) {
108 errno = EINVAL;
109 return NULL;
110 }
111
112 if (sqlodbc_get_conn(name) != NULL) {
113 errno = EEXIST;
114 return NULL;
115 }
116
117 entry = (conn_entry_t *) pcalloc(p, sizeof(conn_entry_t));
118 entry->name = name;
119 entry->data = conn;
120
121 *((conn_entry_t **) push_array(conn_cache)) = entry;
122 return entry;
123 }
124
sqlodbc_timer_cb(CALLBACK_FRAME)125 static int sqlodbc_timer_cb(CALLBACK_FRAME) {
126 register unsigned int i = 0;
127
128 for (i = 0; i < conn_cache->nelts; i++) {
129 conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
130
131 if (entry->timer == p2) {
132 cmd_rec *cmd = NULL;
133
134 sql_log(DEBUG_INFO, "timer expired for connection '%s'", entry->name);
135
136 cmd = pr_cmd_alloc(conn_pool, 2, entry->name, "1");
137 sqlodbc_close(cmd);
138 destroy_pool(cmd->pool);
139
140 entry->timer = 0;
141 }
142 }
143
144 return 0;
145 }
146
147 static SQLCHAR odbc_state[6];
148 static SQLCHAR odbc_errstr[SQL_MAX_MESSAGE_LENGTH];
149
sqlodbc_errstr(SQLSMALLINT handle_type,SQLHANDLE handle,SQLCHAR ** statep)150 static const char *sqlodbc_errstr(SQLSMALLINT handle_type, SQLHANDLE handle,
151 SQLCHAR **statep) {
152 SQLSMALLINT odbc_errlen = 0;
153 SQLINTEGER odbc_errno;
154 SQLRETURN res;
155
156 memset(odbc_state, '\0', sizeof(odbc_state));
157 memset(odbc_errstr, '\0', sizeof(odbc_errstr));
158
159 /* Ideally, we'd keep calling SQLGetDiagRec() until it returned SQL_NO_DATA,
160 * in order to capture the entire error message stack.
161 */
162
163 res = SQLGetDiagRec(handle_type, handle, 1, odbc_state, &odbc_errno,
164 odbc_errstr, sizeof(odbc_errstr), &odbc_errlen);
165
166 if (res != SQL_NO_DATA) {
167 if (statep)
168 *statep = odbc_state;
169
170 return (const char *) odbc_errstr;
171 }
172
173 return "(no data)";
174 }
175
176 /* Liberally borrowed from MySQL-3.23.55's libmysql.c file,
177 * mysql_odbc_escape_string() function.
178 */
sqlodbc_escape_string(char * to,const char * from,size_t fromlen)179 static void sqlodbc_escape_string(char *to, const char *from, size_t fromlen) {
180 const char *end;
181
182 for (end = from + fromlen; from != end; from++) {
183 switch (*from) {
184 case 0:
185 *to++ = '\\';
186 *to++ = '0';
187 break;
188
189 case '\n':
190 *to++ = '\\';
191 *to++ = 'n';
192 break;
193
194 case '\r':
195 *to++ = '\\';
196 *to++ = 'r';
197 break;
198
199 case '\\':
200 *to++ = '\\';
201 *to++ = '\\';
202 break;
203
204 case '\'':
205 *to++ = '\'';
206 *to++ = '\'';
207 break;
208
209 case '"':
210 *to++ = '\\';
211 *to++ = '"';
212 break;
213
214 case '\032':
215 *to++ = '\\';
216 *to++ = 'Z';
217 break;
218
219 default:
220 *to++ = *from;
221 }
222 }
223 }
224
sqlodbc_typestr(SQLSMALLINT type)225 static const char *sqlodbc_typestr(SQLSMALLINT type) {
226 switch (type) {
227 case SQL_CHAR:
228 return "SQL_CHAR";
229
230 case SQL_VARCHAR:
231 return "SQL_VARCHAR";
232
233 case SQL_LONGVARCHAR:
234 return "SQL_LONGVARCHAR";
235
236 #ifdef SQL_WCHAR
237 case SQL_WCHAR:
238 return "SQL_WCHAR";
239 #endif
240
241 #ifdef SQL_WVARCHAR
242 case SQL_WVARCHAR:
243 return "SQL_WVARCHAR";
244 #endif
245
246 #ifdef SQL_WLONGVARCHAR
247 case SQL_WLONGVARCHAR:
248 return "SQL_WLONGVARCHAR";
249 #endif
250
251 case SQL_DECIMAL:
252 return "SQL_DECIMAL";
253
254 case SQL_NUMERIC:
255 return "SQL_NUMERIC";
256
257 case SQL_BIT:
258 return "SQL_BIT";
259
260 case SQL_TINYINT:
261 return "SQL_TINYINT";
262
263 case SQL_SMALLINT:
264 return "SQL_SMALLINT";
265
266 case SQL_INTEGER:
267 return "SQL_INTEGER";
268
269 case SQL_BIGINT:
270 return "SQL_BIGINT";
271
272 case SQL_REAL:
273 return "SQL_REAL";
274
275 case SQL_FLOAT:
276 return "SQL_FLOAT";
277
278 case SQL_DOUBLE:
279 return "SQL_DOUBLE";
280
281 case SQL_BINARY:
282 return "SQL_BINARY";
283
284 case SQL_VARBINARY:
285 return "SQL_VARBINARY";
286
287 case SQL_LONGVARBINARY:
288 return "SQL_LONGVARBINARY";
289
290 case SQL_TYPE_DATE:
291 return "SQL_TYPE_DATE";
292
293 case SQL_TYPE_TIME:
294 return "SQL_TYPE_TIME";
295
296 case SQL_TYPE_TIMESTAMP:
297 return "SQL_TYPE_TIMESTAMP";
298
299 case SQL_GUID:
300 return "SQL_GUID";
301 }
302
303 return "[unknown]";
304 }
305
sqlodbc_strerror(SQLSMALLINT odbc_error)306 static const char *sqlodbc_strerror(SQLSMALLINT odbc_error) {
307 switch (odbc_error) {
308 case SQL_SUCCESS:
309 return "Success";
310
311 case SQL_SUCCESS_WITH_INFO:
312 return "Success with info";
313
314 #ifdef SQL_NO_DATA
315 case SQL_NO_DATA:
316 return "No data";
317 #endif
318
319 case SQL_ERROR:
320 return "Error";
321
322 case SQL_INVALID_HANDLE:
323 return "Invalid handle";
324
325 case SQL_STILL_EXECUTING:
326 return "Still executing";
327
328 case SQL_NEED_DATA:
329 return "Need data";
330 }
331
332 return "(unknown)";
333 }
334
sqlodbc_get_error(cmd_rec * cmd,SQLSMALLINT handle_type,SQLHANDLE handle)335 static modret_t *sqlodbc_get_error(cmd_rec *cmd, SQLSMALLINT handle_type,
336 SQLHANDLE handle) {
337 SQLCHAR state[6], errstr[SQL_MAX_MESSAGE_LENGTH];
338 SQLSMALLINT errlen;
339 SQLINTEGER odbc_errno = 0;
340 SQLRETURN res;
341 unsigned int recno = 1;
342 char numstr[20];
343
344 memset(errstr, '\0', sizeof(errstr));
345 pr_snprintf((char *) errstr, sizeof(errstr)-1, "%s", "(no data)");
346
347 res = SQLGetDiagRec(handle_type, handle, recno++, state, &odbc_errno,
348 errstr, sizeof(errstr), &errlen);
349 while (res != SQL_NO_DATA
350 #ifdef SQL_ERROR
351 && res != SQL_ERROR
352 #endif /* SQL_ERROR */
353 #ifdef SQL_INVALID_HANDLE
354 && res != SQL_INVALID_HANDLE
355 #endif /* SQL_INVALID_HANDLE */
356 ) {
357 pr_signals_handle();
358
359 sql_log(DEBUG_FUNC, "odbc error: [%d] %s", odbc_errno, errstr);
360
361 res = SQLGetDiagRec(handle_type, handle, recno++, state, &odbc_errno,
362 errstr, sizeof(errstr), &errlen);
363 }
364
365 /* This will return the last error retrieved. This is OK, since we
366 * have logged all the previous errors.
367 */
368 memset(numstr, '\0', sizeof(numstr));
369 pr_snprintf(numstr, 20, "%d", (int) odbc_errno);
370
371 return PR_ERROR_MSG(cmd, numstr, (char *) errstr);
372 }
373
sqlodbc_get_data(cmd_rec * cmd,db_conn_t * conn)374 static modret_t *sqlodbc_get_data(cmd_rec *cmd, db_conn_t *conn) {
375 sql_data_t *sd = NULL;
376 array_header *dh = NULL;
377 SQLSMALLINT ncols;
378 SQLRETURN res;
379
380 if (!conn)
381 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
382
383 sd = (sql_data_t *) pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
384
385 res = SQLNumResultCols(conn->sth, &ncols);
386 if (res != SQL_SUCCESS &&
387 res != SQL_SUCCESS_WITH_INFO) {
388 char *err;
389 SQLCHAR *state = NULL;
390
391 err = (char *) sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, &state);
392
393 sql_log(DEBUG_WARN, "error getting column count: %s", err);
394 return PR_ERROR_MSG(cmd, state ? (char *) state : "0", err);
395 }
396 sd->fnum = (unsigned long) ncols;
397 sd->rnum = 0;
398
399 dh = (array_header *) make_array(cmd->tmp_pool, sd->fnum, sizeof(char *));
400
401 /* NOTE: this could be optimised, caching the SQLDescribeCol() results
402 * (used mainly to get the column datatype) so that that call is not
403 * needed for subsequent rows, just the first row.
404 */
405 while (TRUE) {
406 int done_fetching = FALSE;
407 register unsigned int i;
408
409 pr_signals_handle();
410
411 res = SQLFetch(conn->sth);
412
413 switch (res) {
414 case SQL_ERROR:
415 sql_log(DEBUG_WARN, "error fetching row %lu: %s", sd->rnum + 1,
416 sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
417 return PR_ERROR(cmd);
418
419 case SQL_NO_DATA:
420 done_fetching = TRUE;
421 break;
422
423 case SQL_SUCCESS_WITH_INFO:
424 /* Note: this deliberately falls through to the SQL_SUCCESS case. */
425 sql_log(DEBUG_WARN, "fetching row %lu: %s", sd->rnum + 1,
426 sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
427
428 case SQL_SUCCESS:
429 sd->rnum++;
430
431 for (i = 1; i <= sd->fnum; i++) {
432 SQLCHAR col_name[80];
433 SQLSMALLINT col_namelen, col_type, col_digits, col_nullable;
434 SQLULEN col_size;
435
436 if (SQLDescribeCol(conn->sth, i, col_name, sizeof(col_name),
437 &col_namelen, &col_type, &col_size, &col_digits,
438 &col_nullable) == SQL_SUCCESS) {
439 SQLSMALLINT col_ctype;
440
441 /* mod_sql expects to handle all returned data elements as strings
442 * (even though it converts some to numbers), so we need to
443 * stringify any numeric datatypes returned.
444 */
445 switch (col_type) {
446 case SQL_CHAR:
447 case SQL_LONGVARCHAR:
448 case SQL_VARCHAR:
449 #ifdef SQL_WVARCHAR
450 case SQL_WVARCHAR:
451 #endif
452 col_ctype = SQL_C_CHAR;
453
454 if (col_size) {
455 SQLLEN buflen;
456 SQLCHAR *buf = pcalloc(cmd->tmp_pool, ++col_size);
457
458 if (SQLGetData(conn->sth, i, col_ctype, buf, col_size,
459 &buflen) != SQL_SUCCESS) {
460 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
461 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
462 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
463 NULL));
464 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
465 continue;
466 }
467
468 if (buflen == SQL_NO_TOTAL) {
469 sql_log(DEBUG_WARN, "notice: unable to determine total "
470 "number of bytes remaining for %s column %u, row %lu",
471 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
472 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
473 break;
474
475 } else if (buflen == SQL_NULL_DATA) {
476 sql_log(DEBUG_WARN, "notice: data is NULL for %s column "
477 "%u, row %lu", sqlodbc_typestr(col_ctype), i,
478 sd->rnum + 1);
479 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
480 break;
481 }
482
483 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool,
484 (char *) buf);
485
486 } else {
487 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
488 }
489
490 break;
491
492 /* Note: ODBC doesn't seem to provide a way of knowing whether
493 * the value in the column is supposed to be signed or unsigned.
494 * Could cause problems later on.
495 */
496
497 case SQL_TINYINT: {
498 char buf[64];
499 short col_cval;
500 SQLLEN ind;
501
502 col_ctype = SQL_C_TINYINT;
503
504 if (SQLGetData(conn->sth, i, col_ctype,
505 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
506 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
507 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
508 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
509 NULL));
510 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
511 break;
512 }
513
514 if (ind == SQL_NO_TOTAL) {
515 sql_log(DEBUG_WARN, "notice: unable to determine total "
516 "number of bytes remaining for %s column %u, row %lu",
517 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
518 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
519 break;
520
521 } else if (ind == SQL_NULL_DATA) {
522 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
523 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
524 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
525 break;
526 }
527
528 pr_snprintf(buf, sizeof(buf), "%hd", col_cval);
529 buf[sizeof(buf)-1] = '\0';
530
531 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
532 break;
533 }
534
535 case SQL_SMALLINT: {
536 char buf[64];
537 short col_cval;
538 SQLLEN ind;
539
540 col_ctype = SQL_C_SHORT;
541
542 if (SQLGetData(conn->sth, i, col_ctype,
543 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
544 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
545 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
546 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
547 NULL));
548 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
549 break;
550 }
551
552 if (ind == SQL_NO_TOTAL) {
553 sql_log(DEBUG_WARN, "notice: unable to determine total "
554 "number of bytes remaining for %s column %u, row %lu",
555 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
556 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
557 break;
558
559 } else if (ind == SQL_NULL_DATA) {
560 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
561 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
562 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
563 break;
564 }
565
566 pr_snprintf(buf, sizeof(buf), "%hd", col_cval);
567 buf[sizeof(buf)-1] = '\0';
568
569 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
570 break;
571 }
572
573 case SQL_INTEGER: {
574 char buf[64];
575 int col_cval;
576 SQLLEN ind;
577
578 col_ctype = SQL_INTEGER;
579
580 if (SQLGetData(conn->sth, i, col_ctype,
581 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
582 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
583 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
584 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
585 NULL));
586 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
587 break;
588 }
589
590 if (ind == SQL_NO_TOTAL) {
591 sql_log(DEBUG_WARN, "notice: unable to determine total "
592 "number of bytes remaining for %s column %u, row %lu",
593 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
594 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
595 break;
596
597 } else if (ind == SQL_NULL_DATA) {
598 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
599 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
600 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
601 break;
602 }
603
604 pr_snprintf(buf, sizeof(buf), "%d", col_cval);
605 buf[sizeof(buf)-1] = '\0';
606
607 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
608 break;
609 }
610
611 case SQL_BIGINT: {
612 char buf[64];
613 long col_cval;
614 SQLLEN ind;
615
616 col_ctype = SQL_C_LONG;
617
618 if (SQLGetData(conn->sth, i, col_ctype,
619 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
620 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
621 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
622 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
623 NULL));
624 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
625 break;
626 }
627
628 if (ind == SQL_NO_TOTAL) {
629 sql_log(DEBUG_WARN, "notice: unable to determine total "
630 "number of bytes remaining for %s column %u, row %lu",
631 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
632 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
633 break;
634
635 } else if (ind == SQL_NULL_DATA) {
636 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
637 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
638 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
639 break;
640 }
641
642 pr_snprintf(buf, sizeof(buf), "%ld", col_cval);
643 buf[sizeof(buf)-1] = '\0';
644
645 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
646 break;
647 }
648
649 case SQL_REAL: {
650 char buf[64];
651 long col_cval;
652 SQLLEN ind;
653
654 col_ctype = SQL_C_LONG;
655
656 if (SQLGetData(conn->sth, i, col_ctype,
657 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
658 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
659 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
660 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
661 NULL));
662 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
663 break;
664 }
665
666 if (ind == SQL_NO_TOTAL) {
667 sql_log(DEBUG_WARN, "notice: unable to determine total "
668 "number of bytes remaining for %s column %u, row %lu",
669 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
670 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
671 break;
672
673 } else if (ind == SQL_NULL_DATA) {
674 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
675 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
676 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
677 break;
678 }
679
680 pr_snprintf(buf, sizeof(buf), "%ld", col_cval);
681 buf[sizeof(buf)-1] = '\0';
682
683 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
684 break;
685 }
686
687 case SQL_FLOAT: {
688 char buf[64];
689 float col_cval;
690 SQLLEN ind;
691
692 col_ctype = SQL_C_FLOAT;
693
694 if (SQLGetData(conn->sth, i, col_ctype,
695 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
696 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
697 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
698 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
699 NULL));
700 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
701 break;
702 }
703
704 if (ind == SQL_NO_TOTAL) {
705 sql_log(DEBUG_WARN, "notice: unable to determine total "
706 "number of bytes remaining for %s column %u, row %lu",
707 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
708 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
709 break;
710
711 } else if (ind == SQL_NULL_DATA) {
712 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
713 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
714 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
715 break;
716 }
717
718 pr_snprintf(buf, sizeof(buf), "%f", col_cval);
719 buf[sizeof(buf)-1] = '\0';
720
721 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
722 break;
723 }
724
725 case SQL_DOUBLE: {
726 char buf[64];
727 double col_cval;
728 SQLLEN ind;
729
730 col_ctype = SQL_C_DOUBLE;
731
732 if (SQLGetData(conn->sth, i, col_ctype,
733 (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
734 sql_log(DEBUG_WARN, "error getting %s data for column %u, "
735 "row %lu: %s", sqlodbc_typestr(col_ctype), i,
736 sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
737 NULL));
738 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
739 break;
740 }
741
742 if (ind == SQL_NO_TOTAL) {
743 sql_log(DEBUG_WARN, "notice: unable to determine total "
744 "number of bytes remaining for %s column %u, row %lu",
745 sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
746 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
747 break;
748
749 } else if (ind == SQL_NULL_DATA) {
750 sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
751 "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
752 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
753 break;
754 }
755
756 pr_snprintf(buf, sizeof(buf), "%f", col_cval);
757 buf[sizeof(buf)-1] = '\0';
758
759 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
760 break;
761 }
762
763 default:
764 sql_log(DEBUG_WARN, "data type (%s) of column %u not handled",
765 sqlodbc_typestr(col_type), i);
766 }
767
768 } else {
769 sql_log(DEBUG_WARN, "error describing column %u: %s", i,
770 sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
771 }
772 }
773 break;
774
775 default:
776 sql_log(DEBUG_WARN, "SQLFetch returned unhandled result: %d", res);
777 break;
778 }
779
780 if (done_fetching)
781 break;
782 }
783
784 SQLFreeHandle(SQL_HANDLE_STMT, conn->sth);
785 conn->state &= ~SQLODBC_HAVE_STMT_HANDLE;
786
787 /* We allocate an extra char *, for a terminating NULL. */
788 *((char **) push_array(dh)) = NULL;
789 sd->data = (char **) dh->elts;
790
791 return mod_create_data(cmd, (void *) sd);
792 }
793
sqlodbc_open(cmd_rec * cmd)794 MODRET sqlodbc_open(cmd_rec *cmd) {
795 conn_entry_t *entry = NULL;
796 db_conn_t *conn = NULL;
797 SQLSMALLINT res;
798
799 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_open");
800
801 if (cmd->argc < 1) {
802 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
803 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
804 }
805
806 /* Get the named connection. */
807 entry = sqlodbc_get_conn(cmd->argv[0]);
808 if (entry == NULL) {
809 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
810 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
811 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
812 }
813
814 conn = (db_conn_t *) entry->data;
815
816 /* If we're already open (nconn > 0), increment the number of connections.
817 * Reset our timer if we have one, and return HANDLED.
818 */
819 if (entry->nconn > 0) {
820 entry->nconn++;
821
822 if (entry->timer)
823 pr_timer_reset(entry->timer, &sql_odbc_module);
824
825 sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
826 entry->nconn);
827 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
828 return PR_HANDLED(cmd);
829 }
830
831 if (!(conn->state & SQLODBC_HAVE_ENV_HANDLE)) {
832 res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->envh);
833 if (res != SQL_SUCCESS) {
834 sql_log(DEBUG_WARN, "error allocating environment handle: %s",
835 sqlodbc_strerror(res));
836 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
837 return sqlodbc_get_error(cmd, SQL_HANDLE_ENV, conn->envh);
838 }
839
840 res = SQLSetEnvAttr(conn->envh, SQL_ATTR_ODBC_VERSION,
841 (SQLPOINTER) odbc_version, 0);
842 if (res != SQL_SUCCESS) {
843 sql_log(DEBUG_WARN, "error setting SQL_ATTR_ODBC_VERSION %s: %s",
844 odbc_version_str, sqlodbc_strerror(res));
845 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
846 return sqlodbc_get_error(cmd, SQL_HANDLE_ENV, conn->envh);
847 }
848
849 conn->state |= SQLODBC_HAVE_ENV_HANDLE;
850 }
851
852 if (!(conn->state & SQLODBC_HAVE_DBC_HANDLE)) {
853 res = SQLAllocHandle(SQL_HANDLE_DBC, conn->envh, &conn->dbh);
854 if (res != SQL_SUCCESS) {
855 sql_log(DEBUG_WARN, "error allocating database handle: %s",
856 sqlodbc_strerror(res));
857 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
858 return sqlodbc_get_error(cmd, SQL_HANDLE_DBC, conn->dbh);
859 }
860
861 conn->state |= SQLODBC_HAVE_DBC_HANDLE;
862 }
863
864 res = SQLConnect(conn->dbh, (SQLCHAR *) conn->dsn, SQL_NTS,
865 (SQLCHAR *) conn->user, SQL_NTS, (SQLCHAR *) conn->pass, SQL_NTS);
866 if (res != SQL_SUCCESS &&
867 res != SQL_SUCCESS_WITH_INFO) {
868 sql_log(DEBUG_FUNC, "error connecting to dsn '%s': %s", conn->dsn,
869 sqlodbc_strerror(res));
870 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
871 return sqlodbc_get_error(cmd, SQL_HANDLE_DBC, conn->dbh);
872 }
873
874 /* Add some DriverManager and Driver information to the logs via
875 * SQLGetInfo().
876 */
877 if (!(conn->state & SQLODBC_HAVE_INFO)) {
878 SQLCHAR info[SQL_MAX_MESSAGE_LENGTH];
879 SQLSMALLINT infolen;
880
881 res = SQLGetInfo(conn->dbh, SQL_DBMS_NAME, info, sizeof(info), &infolen);
882 if (res == SQL_SUCCESS) {
883 info[infolen] = '\0';
884 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Product name: %s", info);
885
886 /* Oracle does not like use of the LIMIT, and prefers ROWNUM instead.
887 * Thus we need to check to see if this driver is for Oracle.
888 */
889 if (strcasecmp((char *) info, "Oracle") == 0) {
890 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
891 ": %s does not support LIMIT, using ROWNUM instead", info);
892 use_limit_clause = FALSE;
893 use_rownum_clause = TRUE;
894 use_top_clause = FALSE;
895 }
896
897 /* FreeTDS (and SQL Server) does not like either LIMIT or ROWNUM, and
898 * prefers TOP. So we need to check for this as well.
899 */
900 if (strcasecmp((char *) info, "FreeTDS") == 0 ||
901 strstr((char *) info, "SQL Server") != NULL) {
902 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
903 ": %s does not support LIMIT, using TOP instead", info);
904 use_limit_clause = FALSE;
905 use_rownum_clause = FALSE;
906 use_top_clause = TRUE;
907 }
908
909 } else {
910 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
911 ": Product name: (unavailable)");
912 }
913
914 res = SQLGetInfo(conn->dbh, SQL_DBMS_VER, info, sizeof(info), &infolen);
915 if (res == SQL_SUCCESS) {
916 info[infolen] = '\0';
917 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Product version: %s", info);
918
919 } else {
920 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
921 ": Product version: (unavailable)");
922 }
923
924 res = SQLGetInfo(conn->dbh, SQL_DM_VER, info, sizeof(info), &infolen);
925 if (res == SQL_SUCCESS) {
926 info[infolen] = '\0';
927 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver Manager version: %s",
928 info);
929
930 } else {
931 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
932 ": Driver Manager version: (unavailable)");
933 }
934
935 res = SQLGetInfo(conn->dbh, SQL_DRIVER_NAME, info, sizeof(info), &infolen);
936 if (res == SQL_SUCCESS) {
937 info[infolen] = '\0';
938 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver name: %s", info);
939
940 } else {
941 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver name: (unavailable)");
942 }
943
944 res = SQLGetInfo(conn->dbh, SQL_DRIVER_VER, info, sizeof(info), &infolen);
945 if (res == SQL_SUCCESS) {
946 info[infolen] = '\0';
947 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver version: %s", info);
948
949 } else {
950 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
951 ": Driver version: (unavailable)");
952 }
953
954 res = SQLGetInfo(conn->dbh, SQL_DRIVER_ODBC_VER, info, sizeof(info),
955 &infolen);
956 if (res == SQL_SUCCESS) {
957 info[infolen] = '\0';
958 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver ODBC version: %s",
959 info);
960
961 } else {
962 sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
963 ": Driver ODBC version: (unavailable)");
964 }
965
966 conn->state |= SQLODBC_HAVE_INFO;
967 }
968
969 entry->nconn++;
970
971 if (pr_sql_conn_policy == SQL_CONN_POLICY_PERSESSION) {
972 /* If the connection policy is PERSESSION... */
973 if (entry->nconn == 1) {
974 /* ...and we are actually opening the first connection to the database;
975 * we want to make sure this connection stays open, after this first use
976 * (as per Bug#3290). To do this, we re-bump the connection count.
977 */
978 entry->nconn++;
979 }
980
981 } else if (entry->ttl > 0) {
982 /* Set up our timer, if necessary. */
983
984 entry->timer = pr_timer_add(entry->ttl, -1, &sql_odbc_module,
985 sqlodbc_timer_cb, "odbc connection ttl");
986
987 sql_log(DEBUG_INFO, "'%s' connection: %d second timer started",
988 entry->name, entry->ttl);
989
990 /* Timed connections get re-bumped so they don't go away when
991 * sqlodbc_close() is called.
992 */
993 entry->nconn++;
994 }
995
996 sql_log(DEBUG_INFO, "'%s' connection opened", entry->name);
997 sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
998 entry->nconn);
999 pr_event_generate("mod_sql.db.connection-opened", &sql_odbc_module);
1000
1001 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
1002 return PR_HANDLED(cmd);
1003 }
1004
sqlodbc_close(cmd_rec * cmd)1005 MODRET sqlodbc_close(cmd_rec *cmd) {
1006 conn_entry_t *entry = NULL;
1007 db_conn_t *conn = NULL;
1008
1009 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_close");
1010
1011 if (cmd->argc < 1 || cmd->argc > 2) {
1012 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1013 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1014 }
1015
1016 /* Get the named connection. */
1017 entry = sqlodbc_get_conn(cmd->argv[0]);
1018 if (entry == NULL) {
1019 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1020 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1021 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1022 }
1023
1024 conn = (db_conn_t *) entry->data;
1025
1026 /* If we're closed already (nconn == 0), return HANDLED. */
1027 if (entry->nconn == 0) {
1028 sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
1029 entry->nconn);
1030 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1031 return PR_HANDLED(cmd);
1032 }
1033
1034 /* Decrement nconn. If our count is 0 or we received a second arg, close
1035 * the connection, explicitly set the counter to 0, and remove any timers.
1036 */
1037 if ((--entry->nconn) == 0 ||
1038 (cmd->argc == 2 && cmd->argv[1])) {
1039
1040 if (conn->state & SQLODBC_HAVE_STMT_HANDLE) {
1041 SQLFreeHandle(SQL_HANDLE_STMT, conn->sth);
1042 conn->sth = NULL;
1043
1044 conn->state &= ~SQLODBC_HAVE_STMT_HANDLE;
1045 }
1046
1047 if (conn->state & SQLODBC_HAVE_DBC_HANDLE) {
1048 SQLDisconnect(conn->dbh);
1049 SQLFreeHandle(SQL_HANDLE_DBC, conn->dbh);
1050 conn->dbh = NULL;
1051
1052 conn->state &= ~SQLODBC_HAVE_DBC_HANDLE;
1053 }
1054
1055 if (conn->state & SQLODBC_HAVE_ENV_HANDLE) {
1056 SQLFreeHandle(SQL_HANDLE_ENV, conn->envh);
1057 conn->envh = NULL;
1058
1059 conn->state &= ~SQLODBC_HAVE_ENV_HANDLE;
1060 }
1061
1062 entry->nconn = 0;
1063
1064 if (entry->timer) {
1065 pr_timer_remove(entry->timer, &sql_odbc_module);
1066 entry->timer = 0;
1067 sql_log(DEBUG_INFO, "'%s' connection timer stopped", entry->name);
1068 }
1069
1070 sql_log(DEBUG_INFO, "'%s' connection closed", entry->name);
1071 pr_event_generate("mod_sql.db.connection-closed", &sql_odbc_module);
1072 }
1073
1074 sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
1075 entry->nconn);
1076 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1077
1078 return PR_HANDLED(cmd);
1079 }
1080
sqlodbc_def_conn(cmd_rec * cmd)1081 MODRET sqlodbc_def_conn(cmd_rec *cmd) {
1082 char *name = NULL;
1083 conn_entry_t *entry = NULL;
1084 db_conn_t *conn = NULL;
1085
1086 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_defineconnection");
1087
1088 if (cmd->argc < 4 ||
1089 cmd->argc > 10 ||
1090 !cmd->argv[0]) {
1091 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1092 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1093 }
1094
1095 conn = (db_conn_t *) pcalloc(conn_pool, sizeof(db_conn_t));
1096
1097 name = pstrdup(conn_pool, cmd->argv[0]);
1098 conn->user = pstrdup(conn_pool, cmd->argv[1]);
1099 conn->pass = pstrdup(conn_pool, cmd->argv[2]);
1100 conn->dsn = pstrdup(conn_pool, cmd->argv[3]);
1101
1102 /* Insert the new conn_info into the connection hash */
1103 entry = sqlodbc_add_conn(conn_pool, name, (void *) conn);
1104 if (entry == NULL &&
1105 errno == EEXIST) {
1106 /* Log only connections named other than "default", for debugging
1107 * misconfigurations with multiple different SQLNamedConnectInfo
1108 * directives using the same name.
1109 */
1110 if (strcmp(name, "default") != 0) {
1111 sql_log(DEBUG_FUNC, "named connection '%s' already exists", name);
1112 }
1113
1114 entry = sqlodbc_get_conn(name);
1115 }
1116
1117 if (entry == NULL) {
1118 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1119 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1120 "error adding named connection");
1121 }
1122
1123 if (cmd->argc >= 5) {
1124 entry->ttl = (int) strtol(cmd->argv[4], (char **) NULL, 10);
1125 if (entry->ttl >= 1) {
1126 pr_sql_conn_policy = SQL_CONN_POLICY_TIMER;
1127
1128 } else {
1129 entry->ttl = 0;
1130 }
1131 }
1132
1133 entry->timer = 0;
1134 entry->nconn = 0;
1135
1136 sql_log(DEBUG_INFO, " name: '%s'", entry->name);
1137 sql_log(DEBUG_INFO, " user: '%s'", conn->user);
1138 sql_log(DEBUG_INFO, " dsn: '%s'", conn->dsn);
1139
1140 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1141 return PR_HANDLED(cmd);
1142 }
1143
sqlodbc_select(cmd_rec * cmd)1144 MODRET sqlodbc_select(cmd_rec *cmd) {
1145 conn_entry_t *entry = NULL;
1146 db_conn_t *conn = NULL;
1147 modret_t *mr = NULL;
1148 char *query = NULL;
1149 cmd_rec *close_cmd;
1150
1151 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_select");
1152
1153 if (cmd->argc < 2) {
1154 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1155 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1156 }
1157
1158 /* Get the named connection. */
1159 entry = sqlodbc_get_conn(cmd->argv[0]);
1160 if (entry == NULL) {
1161 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1162 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1163 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1164 }
1165
1166 conn = (db_conn_t *) entry->data;
1167
1168 mr = sqlodbc_open(cmd);
1169 if (MODRET_ERROR(mr)) {
1170 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1171 return mr;
1172 }
1173
1174 /* Construct the query string. */
1175 if (cmd->argc == 2) {
1176 query = pstrcat(cmd->tmp_pool, "SELECT ", cmd->argv[1], NULL);
1177
1178 } else {
1179 query = pstrcat(cmd->tmp_pool, cmd->argv[2], " FROM ", cmd->argv[1], NULL);
1180
1181 if (cmd->argc > 3 &&
1182 cmd->argv[3])
1183 query = pstrcat(cmd->tmp_pool, query, " WHERE ", cmd->argv[3], NULL);
1184
1185 if (cmd->argc > 4 &&
1186 cmd->argv[4]) {
1187
1188 if (use_limit_clause) {
1189 query = pstrcat(cmd->tmp_pool, query, " LIMIT ", cmd->argv[4], NULL);
1190
1191 } else if (use_rownum_clause) {
1192 query = pstrcat(cmd->tmp_pool, query, " AND ROWNUM = ", cmd->argv[4],
1193 NULL);
1194
1195 } else if (use_top_clause) {
1196 /* Note that the TOP clause must be at the start of the query, not
1197 * the end.
1198 */
1199 query = pstrcat(cmd->tmp_pool, "TOP ", cmd->argv[4], " ", query, NULL);
1200 }
1201 }
1202
1203 if (cmd->argc > 5) {
1204 register unsigned int i = 0;
1205
1206 /* Handle the optional arguments -- they're rare, so in this case
1207 * we'll play with the already constructed query string, but in
1208 * general we should probably take optional arguments into account
1209 * and put the query string together later once we know what they are.
1210 */
1211
1212 for (i = 5; i < cmd->argc; i++) {
1213 if (cmd->argv[i] &&
1214 strcasecmp("DISTINCT", cmd->argv[i]) == 0)
1215 query = pstrcat(cmd->tmp_pool, "DISTINCT ", query, NULL);
1216 }
1217 }
1218
1219 query = pstrcat(cmd->tmp_pool, "SELECT ", query, NULL);
1220 }
1221
1222 /* Log the query string */
1223 sql_log(DEBUG_INFO, "query \"%s\"", query);
1224
1225 if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1226 if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1227 SQL_SUCCESS) {
1228 sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1229 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1230 return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1231 }
1232
1233 conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1234 }
1235
1236 /* Perform the query. If it doesn't work, log the error, close the
1237 * connection, then return the error from the query processing.
1238 */
1239 switch (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query))) {
1240 case SQL_SUCCESS:
1241 case SQL_SUCCESS_WITH_INFO:
1242 break;
1243
1244 default:
1245 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1246
1247 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1248 sqlodbc_close(close_cmd);
1249 destroy_pool(close_cmd->pool);
1250
1251 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1252 return mr;
1253 }
1254
1255 switch (SQLExecute(conn->sth)) {
1256 case SQL_SUCCESS:
1257 case SQL_SUCCESS_WITH_INFO:
1258 break;
1259
1260 default:
1261 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1262
1263 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1264 sqlodbc_close(close_cmd);
1265 destroy_pool(close_cmd->pool);
1266
1267 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1268 return mr;
1269 }
1270
1271 /* Get the data. If it doesn't work, log the error, close the connection,
1272 * then return the error from the data processing.
1273 */
1274
1275 mr = sqlodbc_get_data(cmd, conn);
1276 if (MODRET_ERROR(mr)) {
1277 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1278 sqlodbc_close(close_cmd);
1279 destroy_pool(close_cmd->pool);
1280
1281 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1282 return mr;
1283 }
1284
1285 /* Close the connection, return the data. */
1286 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1287 sqlodbc_close(close_cmd);
1288 destroy_pool(close_cmd->pool);
1289
1290 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1291 return mr;
1292 }
1293
sqlodbc_insert(cmd_rec * cmd)1294 MODRET sqlodbc_insert(cmd_rec *cmd) {
1295 conn_entry_t *entry = NULL;
1296 db_conn_t *conn = NULL;
1297 modret_t *mr = NULL;
1298 char *query = NULL;
1299 cmd_rec *close_cmd;
1300
1301 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_insert");
1302
1303 if (cmd->argc != 2 && cmd->argc != 4) {
1304 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1305 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1306 }
1307
1308 /* Get the named connection. */
1309 entry = sqlodbc_get_conn(cmd->argv[0]);
1310 if (entry == NULL) {
1311 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1312 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1313 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1314 }
1315
1316 conn = (db_conn_t *) entry->data;
1317
1318 mr = sqlodbc_open(cmd);
1319 if (MODRET_ERROR(mr)) {
1320 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1321 return mr;
1322 }
1323
1324 /* Construct the query string */
1325 if (cmd->argc == 2)
1326 query = pstrcat(cmd->tmp_pool, "INSERT ", cmd->argv[1], NULL);
1327
1328 else
1329 query = pstrcat(cmd->tmp_pool, "INSERT INTO ", cmd->argv[1], " (",
1330 cmd->argv[2], ") VALUES (", cmd->argv[3], ")", NULL);
1331
1332 /* Log the query string */
1333 sql_log(DEBUG_INFO, "query \"%s\"", query);
1334
1335 if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1336 if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1337 SQL_SUCCESS) {
1338 sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1339 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1340 return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1341 }
1342
1343 conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1344 }
1345
1346 /* Perform the query. If it doesn't work, log the error, close the
1347 * connection (and log any errors there, too) then return the error
1348 * from the query processing.
1349 */
1350 if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1351 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1352
1353 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1354 sqlodbc_close(close_cmd);
1355 destroy_pool(close_cmd->pool);
1356
1357 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1358 return mr;
1359 }
1360
1361 switch (SQLExecute(conn->sth)) {
1362 case SQL_SUCCESS:
1363 case SQL_SUCCESS_WITH_INFO:
1364 break;
1365
1366 default:
1367 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1368
1369 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1370 sqlodbc_close(close_cmd);
1371 destroy_pool(close_cmd->pool);
1372
1373 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1374 return mr;
1375 }
1376
1377 /* Close the connection and return HANDLED. */
1378 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1379 sqlodbc_close(close_cmd);
1380 destroy_pool(close_cmd->pool);
1381
1382 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1383 return PR_HANDLED(cmd);
1384 }
1385
sqlodbc_update(cmd_rec * cmd)1386 MODRET sqlodbc_update(cmd_rec *cmd) {
1387 conn_entry_t *entry = NULL;
1388 db_conn_t *conn = NULL;
1389 modret_t *mr = NULL;
1390 char *query = NULL;
1391 cmd_rec *close_cmd;
1392
1393 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_update");
1394
1395 if (cmd->argc < 2 || cmd->argc > 4) {
1396 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1397 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1398 }
1399
1400 /* Get the named connection. */
1401 entry = sqlodbc_get_conn(cmd->argv[0]);
1402 if (entry == NULL) {
1403 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1404 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1405 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1406 }
1407
1408 conn = (db_conn_t *) entry->data;
1409
1410 mr = sqlodbc_open(cmd);
1411 if (MODRET_ERROR(mr)) {
1412 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1413 return mr;
1414 }
1415
1416 if (cmd->argc == 2) {
1417 query = pstrcat(cmd->tmp_pool, "UPDATE ", cmd->argv[1], NULL);
1418
1419 } else {
1420 /* Construct the query string */
1421 query = pstrcat(cmd->tmp_pool, "UPDATE ", cmd->argv[1], " SET ",
1422 cmd->argv[2], NULL);
1423
1424 if (cmd->argc > 3 && cmd->argv[3])
1425 query = pstrcat(cmd->tmp_pool, query, " WHERE ", cmd->argv[3], NULL);
1426 }
1427
1428 /* Log the query string. */
1429 sql_log(DEBUG_INFO, "query \"%s\"", query);
1430
1431 if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1432 if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1433 SQL_SUCCESS) {
1434 sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1435 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1436 return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1437 }
1438
1439 conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1440 }
1441
1442 /* Perform the query. If it doesn't work close the connection, then
1443 * return the error from the query processing.
1444 */
1445 if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1446 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1447
1448 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1449 sqlodbc_close(close_cmd);
1450 destroy_pool(close_cmd->pool);
1451
1452 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1453 return mr;
1454 }
1455
1456 switch (SQLExecute(conn->sth)) {
1457 case SQL_SUCCESS:
1458 case SQL_SUCCESS_WITH_INFO:
1459 break;
1460
1461 default:
1462 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1463
1464 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1465 sqlodbc_close(close_cmd);
1466 destroy_pool(close_cmd->pool);
1467
1468 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1469 return mr;
1470 }
1471
1472 /* Close the connection, return HANDLED. */
1473 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1474 sqlodbc_close(close_cmd);
1475 destroy_pool(close_cmd->pool);
1476
1477 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1478 return PR_HANDLED(cmd);
1479 }
1480
sqlodbc_procedure(cmd_rec * cmd)1481 MODRET sqlodbc_procedure(cmd_rec *cmd) {
1482 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_procedure");
1483
1484 if (cmd->argc != 3) {
1485 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_procedure");
1486 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1487 }
1488
1489 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_procedure");
1490 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1491 "backend does not support procedures");
1492 }
1493
sqlodbc_query(cmd_rec * cmd)1494 MODRET sqlodbc_query(cmd_rec *cmd) {
1495 conn_entry_t *entry = NULL;
1496 db_conn_t *conn = NULL;
1497 modret_t *mr = NULL;
1498 char *query = NULL;
1499 cmd_rec *close_cmd;
1500
1501 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_query");
1502
1503 if (cmd->argc != 2) {
1504 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1505 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1506 }
1507
1508 /* Get the named connection. */
1509 entry = sqlodbc_get_conn(cmd->argv[0]);
1510 if (entry == NULL) {
1511 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1512 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1513 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1514 }
1515
1516 conn = (db_conn_t *) entry->data;
1517
1518 mr = sqlodbc_open(cmd);
1519 if (MODRET_ERROR(mr)) {
1520 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1521 return mr;
1522 }
1523
1524 query = pstrcat(cmd->tmp_pool, cmd->argv[1], NULL);
1525
1526 /* Log the query string */
1527 sql_log(DEBUG_INFO, "query \"%s\"", query);
1528
1529 if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1530 if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1531 SQL_SUCCESS) {
1532 sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1533 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1534 return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1535 }
1536
1537 conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1538 }
1539
1540 /* Perform the query. If it doesn't work close the connection, then
1541 * return the error from the query processing.
1542 */
1543 if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1544 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1545
1546 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1547 sqlodbc_close(close_cmd);
1548 destroy_pool(close_cmd->pool);
1549
1550 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1551 return mr;
1552 }
1553
1554 switch (SQLExecute(conn->sth)) {
1555 case SQL_SUCCESS:
1556 case SQL_SUCCESS_WITH_INFO:
1557 break;
1558
1559 default:
1560 mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1561
1562 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1563 sqlodbc_close(close_cmd);
1564 destroy_pool(close_cmd->pool);
1565
1566 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1567 return mr;
1568 }
1569
1570 /* Get data if necessary. If it doesn't work, log the error, close the
1571 * connection then return the error from the data processing.
1572 */
1573
1574 mr = sqlodbc_get_data(cmd, conn);
1575 if (MODRET_ERROR(mr)) {
1576 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1577
1578 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1579 sqlodbc_close(close_cmd);
1580 destroy_pool(close_cmd->pool);
1581
1582 return mr;
1583 }
1584
1585 /* Close the connection, return the data. */
1586 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1587 sqlodbc_close(close_cmd);
1588 destroy_pool(close_cmd->pool);
1589
1590 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1591 return mr;
1592 }
1593
1594 /* XXX need to provide an escapestring implementation, probably borrowed
1595 * from MySQL. Make sure it's standards-compliant.
1596 */
1597
sqlodbc_quote(cmd_rec * cmd)1598 MODRET sqlodbc_quote(cmd_rec *cmd) {
1599 conn_entry_t *entry = NULL;
1600 modret_t *mr = NULL;
1601 char *unescaped = NULL;
1602 char *escaped = NULL;
1603 cmd_rec *close_cmd;
1604
1605 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_escapestring");
1606
1607 if (cmd->argc != 2) {
1608 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1609 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1610 }
1611
1612 /* Get the named connection. */
1613 entry = sqlodbc_get_conn(cmd->argv[0]);
1614 if (entry == NULL) {
1615 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1616 return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1617 pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1618 }
1619
1620 /* Make sure the connection is open. */
1621 mr = sqlodbc_open(cmd);
1622 if (MODRET_ERROR(mr)) {
1623 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1624 return mr;
1625 }
1626
1627 unescaped = cmd->argv[1];
1628 escaped = (char *) pcalloc(cmd->tmp_pool, sizeof(char) *
1629 (strlen(unescaped) * 2) + 1);
1630
1631 sqlodbc_escape_string(escaped, unescaped, strlen(unescaped));
1632
1633 close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1634 sqlodbc_close(close_cmd);
1635 destroy_pool(close_cmd->pool);
1636
1637 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1638 return mod_create_data(cmd, (void *) escaped);
1639 }
1640
sqlodbc_exit(cmd_rec * cmd)1641 MODRET sqlodbc_exit(cmd_rec *cmd) {
1642 register unsigned int i = 0;
1643
1644 sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_exit");
1645
1646 for (i = 0; i < conn_cache->nelts; i++) {
1647 conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
1648
1649 if (entry->nconn > 0) {
1650 cmd_rec *close_cmd = pr_cmd_alloc(conn_pool, 2, entry->name, "1");
1651 sqlodbc_close(close_cmd);
1652 destroy_pool(close_cmd->pool);
1653 }
1654 }
1655
1656 sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_exit");
1657 return PR_HANDLED(cmd);
1658 }
1659
sqlodbc_identify(cmd_rec * cmd)1660 MODRET sqlodbc_identify(cmd_rec *cmd) {
1661 sql_data_t *sd = NULL;
1662
1663 sd = (sql_data_t *) pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
1664 sd->data = (char **) pcalloc(cmd->tmp_pool, sizeof(char *) * 2);
1665
1666 sd->rnum = 1;
1667 sd->fnum = 2;
1668
1669 sd->data[0] = MOD_SQL_ODBC_VERSION;
1670 sd->data[1] = MOD_SQL_API_V1;
1671
1672 return mod_create_data(cmd, (void *) sd);
1673 }
1674
1675 /* mod_sql-specific command dispatch/handler table */
1676 static cmdtable sqlodbc_cmdtable[] = {
1677 { CMD, "sql_open", G_NONE, sqlodbc_open, FALSE, FALSE },
1678 { CMD, "sql_close", G_NONE, sqlodbc_close, FALSE, FALSE },
1679 { CMD, "sql_defineconnection",G_NONE, sqlodbc_def_conn, FALSE, FALSE },
1680 { CMD, "sql_select", G_NONE, sqlodbc_select, FALSE, FALSE },
1681 { CMD, "sql_insert", G_NONE, sqlodbc_insert, FALSE, FALSE },
1682 { CMD, "sql_update", G_NONE, sqlodbc_update, FALSE, FALSE },
1683 { CMD, "sql_procedure", G_NONE, sqlodbc_procedure, FALSE, FALSE },
1684 { CMD, "sql_query", G_NONE, sqlodbc_query, FALSE, FALSE },
1685 { CMD, "sql_escapestring", G_NONE, sqlodbc_quote, FALSE, FALSE },
1686 { CMD, "sql_exit", G_NONE, sqlodbc_exit, FALSE, FALSE },
1687 { CMD, "sql_identify", G_NONE, sqlodbc_identify, FALSE, FALSE },
1688
1689 { 0, NULL }
1690 };
1691
1692 /* Configuration handlers
1693 */
1694
1695 /* usage: SQLODBCVersion version */
set_sqlodbcversion(cmd_rec * cmd)1696 MODRET set_sqlodbcversion(cmd_rec *cmd) {
1697 int version = -1;
1698 const char *version_str;
1699 config_rec *c;
1700
1701 CHECK_ARGS(cmd, 1);
1702 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
1703
1704 if (strcasecmp(cmd->argv[1], "2") == 0 ||
1705 strcasecmp(cmd->argv[1], "odbcv2") == 0) {
1706 #if defined(SQL_OV_ODBC2)
1707 version = SQL_OV_ODBC2;
1708 version_str = "ODBCv2";
1709 #endif /* ODBCv2 */
1710
1711 } else if (strcasecmp(cmd->argv[1], "3") == 0 ||
1712 strcasecmp(cmd->argv[1], "odbcv3") == 0) {
1713 #if defined(SQL_OV_ODBC3)
1714 version = SQL_OV_ODBC3;
1715 version_str = "ODBCv3";
1716 #endif /* ODBCv3 */
1717
1718 } else if (strcasecmp(cmd->argv[1], "3.80") == 0 ||
1719 strcasecmp(cmd->argv[1], "odbcv3.80") == 0) {
1720 #if defined(SQL_OV_ODBC3_80)
1721 version = SQL_OV_ODBC3_80;
1722 version_str = "ODBCv3.80";
1723 #endif /* ODBCv3.80 */
1724 }
1725
1726 if (version < 0) {
1727 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1728 "unknown/supported ODBC API version: ", cmd->argv[1], NULL));
1729 }
1730
1731 c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1732 c->argv[0] = palloc(c->pool, sizeof(int));
1733 *((int *) c->argv[0]) = version;
1734 c->argv[1] = pstrdup(c->pool, version_str);
1735
1736 return PR_HANDLED(cmd);
1737 }
1738
1739 /* Event handlers
1740 */
1741
sqlodbc_mod_unload_ev(const void * event_data,void * user_data)1742 static void sqlodbc_mod_unload_ev(const void *event_data, void *user_data) {
1743
1744 if (strcmp("mod_sql_odbc.c", (const char *) event_data) == 0) {
1745 /* Unegister ourselves with mod_sql. */
1746 if (sql_unregister_backend("odbc") < 0) {
1747 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_ODBC_VERSION
1748 ": notice: error unregistering backend: %s", strerror(errno));
1749 pr_session_end(0);
1750 }
1751
1752 /* Unregister ourselves from all events. */
1753 pr_event_unregister(&sql_odbc_module, NULL, NULL);
1754 }
1755 }
1756
1757 /* Module initialization routines
1758 */
1759
sqlodbc_init(void)1760 static int sqlodbc_init(void) {
1761
1762 /* Register ourselves with mod_sql. */
1763 if (sql_register_backend("odbc", sqlodbc_cmdtable) < 0) {
1764 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_ODBC_VERSION
1765 ": notice: error registering backend: %s", strerror(errno));
1766 errno = EPERM;
1767 return -1;
1768 }
1769
1770 /* Register listeners for the unload event. */
1771 pr_event_register(&sql_odbc_module, "core.module-unload",
1772 sqlodbc_mod_unload_ev, NULL);
1773
1774 return 0;
1775 }
1776
sqlodbc_sess_init(void)1777 static int sqlodbc_sess_init(void) {
1778 config_rec *c;
1779
1780 if (conn_pool != NULL) {
1781 destroy_pool(conn_pool);
1782 conn_cache = NULL;
1783 }
1784
1785 conn_pool = make_sub_pool(session.pool);
1786 pr_pool_tag(conn_pool, "ODBC connection pool");
1787
1788 if (conn_cache == NULL) {
1789 conn_cache = make_array(conn_pool, DEF_CONN_POOL_SIZE,
1790 sizeof(conn_entry_t *));
1791 }
1792
1793 /* There is a very specific reason for using square brackets, rather than
1794 * parentheses, here.
1795 *
1796 * Users of the mod_sql_odbc module for talking to an Oracle database
1797 * via ODBC encountered a bug in the Oracle client library, where the Oracle
1798 * client library tries to find the name of the process calling the client,
1799 * and adds that name to the connection string used. However, that process
1800 * name parsing will fail if the process name uses parentheses. The
1801 * workaround, then, is to use square brackets.
1802 *
1803 * For those curious, this is Oracle Bug 3807408. More discussions on
1804 * problem can be found at:
1805 *
1806 * http://forums.oracle.com/forums/thread.jspa?threadID=296725
1807 *
1808 * The following comment indicates that more recent versions of Oracle
1809 * have this issue fixed; there is also a patch available:
1810 *
1811 * "This issue is Oracle bug 3807408 and can be fixed by applying 10.2.0.1
1812 * Patch 6. This can be downloaded from MetaLink if you have an account:
1813 *
1814 * http://updates.oracle.com/ARULink/PatchDetails/process_form?patch_num=5059238"
1815 * This comment was found at:
1816 *
1817 * http://www.topxml.com/forum/Database_Adapter_for_Oracle_on_64bit_Windows/m_3448/tm.htm
1818 */
1819
1820 pr_proctitle_set("[accepting connections]");
1821
1822 c = find_config(main_server->conf, CONF_PARAM, "SQLODBCVersion", FALSE);
1823 if (c != NULL) {
1824 odbc_version = *((int *) c->argv[0]);
1825 odbc_version_str = c->argv[1];
1826 }
1827
1828 return 0;
1829 }
1830
1831 /* Module API tables
1832 */
1833
1834 static conftable sqlodbc_conftab[] = {
1835 { "SQLODBCVersion", set_sqlodbcversion, NULL },
1836 { NULL, NULL, NULL }
1837 };
1838
1839 module sql_odbc_module = {
1840 NULL, NULL,
1841
1842 /* Module API version */
1843 0x20,
1844
1845 /* Module name */
1846 "sql_odbc",
1847
1848 /* Module configuration directive table */
1849 sqlodbc_conftab,
1850
1851 /* Module command handler table */
1852 NULL,
1853
1854 /* Module authentication handler table */
1855 NULL,
1856
1857 /* Module initialization */
1858 sqlodbc_init,
1859
1860 /* Session initialization */
1861 sqlodbc_sess_init,
1862
1863 /* Module version */
1864 MOD_SQL_ODBC_VERSION
1865 };
1866