diff options
Diffstat (limited to 'src/vmod-sql.c')
-rw-r--r-- | src/vmod-sql.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/src/vmod-sql.c b/src/vmod-sql.c new file mode 100644 index 0000000..119fa25 --- /dev/null +++ b/src/vmod-sql.c @@ -0,0 +1,294 @@ +/* This file is part of vmod-sql + Copyright (C) 2013 Sergey Poznyakoff + + Vmod-sql is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Vmod-sql is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with vmod-sql. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "vmod-sql.h" +#include <stdarg.h> +#include "wordsplit.h" +#include "vrt.h" +#include "vcc_if.h" +#include "bin/varnishd/cache.h" + +static pthread_once_t thread_once = PTHREAD_ONCE_INIT; +static pthread_key_t thread_key; + +void +i_modsql_debug(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap); + va_end(ap); +} + +void +i_modsql_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap); + va_end(ap); +} + +struct vmod_sql_connection * +i_modsql_conntab_get(int n) +{ + struct vmod_sql_connection *conntab = pthread_getspecific(thread_key); + if (n < 0 || n > MAXCONN || !conntab || !conntab[n].backend) + return NULL; + return conntab + n; +} + +int +i_modsql_conntab_alloc(struct vmod_sql_connection *cp) +{ + int i; + struct vmod_sql_connection *conntab = pthread_getspecific(thread_key); + if (!conntab) { + conntab = calloc(MAXCONN, sizeof(conntab[0])); + AN(conntab); + AZ(pthread_setspecific(thread_key, conntab)); + } + + for (i = 0; i < MAXCONN; i++) { + if (conntab[i].backend == NULL) { + conntab[i] = *cp; + return i; + } + } + return -1; +} + +static int +i_modsql_conntab_find(const char *bkname, const char *param_str) +{ + int i; + struct vmod_sql_connection *conntab = pthread_getspecific(thread_key); + if (!conntab) + return -1; + for (i = 0; i < MAXCONN; i++) { + if (conntab[i].backend && + strcmp(conntab[i].backend->name, bkname) == 0 && + strcmp(conntab[i].param_str, param_str) == 0) { + return i; + } + } + return -1; +} + + +static void +conn_free(void *f) +{ + struct vmod_sql_connection *conntab = pthread_getspecific(thread_key); + if (conntab) { + struct vmod_sql_connection *cp; + for (cp = conntab; cp < conntab + MAXCONN; cp++) { + if (cp->state != state_init) { + i_modsql_disconnect(cp); + i_modsql_destroy(cp); + } + } + } + free(conntab); +} + +static void +make_key() +{ + pthread_key_create(&thread_key, conn_free); +} + +int +module_init(struct vmod_priv *priv, const struct VCL_conf *vclconf) +{ + pthread_once(&thread_once, make_key); + return 0; +} + +char * +i_modsql_findparam(char **params, char *name) +{ + char *p, *q; + + while (*params) { + p = *params++; + for (q = name; *p && *q && *p == *q; p++, q++); + if (*q == 0 && *p == '=') + return p+1; + } + return NULL; +} + + +static struct vmod_sql_backend *bcktab[] = { +#ifdef USE_SQL_MYSQL + &i_modsql_mysql_backend, +#endif +#ifdef USE_SQL_PGSQL + &i_modsql_pgsql_backend, +#endif + NULL +}; + +static struct vmod_sql_backend * +find_backend(const char *name) +{ + int i; + + for (i = 0; bcktab[i]; i++) { + if (strcmp(bcktab[i]->name, name) == 0) + return bcktab[i]; + } + return NULL; +} + +int +vmod_connect(struct sess *sp, struct vmod_priv *priv, const char *bkname, + const char *param) +{ + struct wordsplit ws; + struct vmod_sql_backend *be; + int n; + + n = i_modsql_conntab_find(bkname, param); + if (n != -1) + return n; + ws.ws_delim = ";"; + if (wordsplit(param, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE | + WRDSF_CESCAPES | + WRDSF_DELIM)) { + i_modsql_error("cannot split string `%s': %s", + param, wordsplit_strerror(&ws)); + return -1; + } + + /* Select backend */ + be = find_backend(bkname); + if (!be) { + i_modsql_error("unsupported backend: %s", bkname); + wordsplit_free(&ws); + return -1; + } + + n = i_modsql_create_connection(be, param, ws.ws_wordv); + ws.ws_wordc = 0; + ws.ws_wordv = NULL; + wordsplit_free(&ws); + return n; +} + +void +vmod_connect_init(struct sess *sp, struct vmod_priv *priv, const char *bkname, + const char *param) +{ + AZ(vmod_connect(sp, priv, bkname, param)); +} + +static struct vmod_sql_connection * +runquery(struct sess *sp, int n, const char *query, const char *arg) +{ + struct vmod_sql_connection *cp; + struct wordsplit ws, wsenv; + int i, rc; + + cp = i_modsql_conntab_get(n); + if (!cp) + return NULL; + debug(cp, 2, ("runquery(%s) begin", arg)); + + if (cp->state != state_connected) + return NULL; + + debug(cp, 2, ("runquery: splitting arg")); + wsenv.ws_delim = ";"; + if (wordsplit(arg, &wsenv, WRDSF_NOVAR|WRDSF_NOCMD|WRDSF_DELIM)) { + i_modsql_error("cannot split string `%s': %s", + arg, wordsplit_strerror(&wsenv)); + return NULL; + } + + if (cp->backend->be_escape) { + debug(cp, 2, ("escaping variables")); + for (i = 0; i < wsenv.ws_wordc; i++) { + char *p = i_modsql_escape(cp, wsenv.ws_wordv[i]); + if (!p) { + i_modsql_error("cannot expand argument"); + wordsplit_free(&wsenv); + return NULL; + } + free(wsenv.ws_wordv[i]); + wsenv.ws_wordv[i] = p; + debug(cp, 3, ("%d: %s",i,p)); + } + } + + debug(cp, 2, ("expanding query")); + ws.ws_env = (const char **)wsenv.ws_wordv; + rc = wordsplit(query, &ws, + WRDSF_NOCMD | WRDSF_QUOTE | + WRDSF_NOSPLIT | + WRDSF_ENV | WRDSF_UNDEF); + if (rc) { + i_modsql_error("cannot expand query `%s': %s", + query, wordsplit_strerror(&ws)); + wordsplit_free(&wsenv); + return NULL; + } + + rc = i_modsql_query(cp, ws.ws_wordv[0]); + wordsplit_free(&ws); + wordsplit_free(&wsenv); + + return cp; +} + +unsigned +vmod_query(struct sess *sp, struct vmod_priv *priv, int cd, const char *query, + const char *arg) +{ + return !runquery(sp, cd, query, arg); +} + +const char * +vmod_result(struct sess *sp, struct vmod_priv *priv, int cd, int row, int col) +{ + struct vmod_sql_connection *cp; + const char *s; + + cp = i_modsql_conntab_get(cd); + if (!cp) + return NULL; + + s = i_modsql_get_column(cp, row, col); + + if (!s) + return NULL; + return WS_Dup(sp->ws, s); +} + +int +vmod_affected(struct sess *sp, struct vmod_priv *priv, int cd) +{ + struct vmod_sql_connection *cp; + + cp = i_modsql_conntab_get(cd); + if (!cp) + return 0; + return i_modsql_affected_rows(cp); +} + + |