From 7111da4edcbff6f8a27af53bb340e31ea3c25867 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 14 Jul 2013 15:49:09 +0300 Subject: Add PostgreSQL support. --- README | 5 +- configure.ac | 71 +++++++++++----------- src/pgsql.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 224 insertions(+), 43 deletions(-) diff --git a/README b/README index b177149..a7f7636 100644 --- a/README +++ b/README @@ -8,8 +8,7 @@ structure, considerably speeding up their handling. Another advantage of this approach is that rewrite rules stored in a database are easier to maintain. -This version supports MySQL databases. Support for PostgreSQL will be -added soon. +This version supports MySQL and PostgreSQL databases. * Example @@ -37,7 +36,7 @@ sub vcl_recv { set req.http.X-Redirect-To = dbrw.rewrite("host=" + req.http.Host + ";" + "url=" + req.url); - if (req.http.X-Redirect-To) { + if (req.http.X-Redirect-To != "") { error(750, "Redirect"); } } diff --git a/configure.ac b/configure.ac index 1ace670..09d56fa 100644 --- a/configure.ac +++ b/configure.ac @@ -96,61 +96,58 @@ fi ########### # Check for SQL support -build_mysqlmap=probe -build_pgsqlmap=no #FIXME +build_mysql=probe +build_pgsql=probe AC_ARG_WITH(mysql, AC_HELP_STRING([--without-mysql], [Configure to work without MySQL]), - [build_mysqlmap=$withval]) + [build_mysql=$withval]) AC_ARG_WITH(pgsql, AC_HELP_STRING([--without-pgsql], [Configure to work without Postgres]), - [build_pgsqlmap=$withval]) + [build_pgsql=$withval]) AC_ARG_WITH(postgres, AC_HELP_STRING([--without-postgres], [Same as --without-pgsql]), - [build_pgsqlmap=$withval]) + [build_pgsql=$withval]) -GR_ENABLE(mysqlmap, [ +GR_ENABLE(mysql, [ GR_CHECK_LIB(mysqlclient, mysql_real_connect, "-lm", [ AC_DEFINE(USE_SQL_MYSQL,1, [Define this if you are going to use MySQL]) AC_DEFINE(HAVE_LIBMYSQL,1, [Define this if you have mysqlclient library]) MYSQLLIBS="$pm_cv_lib_mysqlclient" - SQL_MODULES="$SQL_MODULES mysqlmap.la" - build_mysqlmap=yes ], - [ test "$build_mysqlmap" = yes && AC_MSG_ERROR([cannot build the requested module mysqlmap, because MySQL libraries are not present]) - build_mysqlmap=no ], - [/usr/lib/mysql /usr/local/lib/mysql])], $build_mysqlmap) - -dnl FIXME -dnl GR_ENABLE(pgsqlmap, [ -dnl GR_CHECK_LIB(pq, PQconnectStart, [], -dnl [ save_CPPFLAGS=$CPPFLAGS -dnl for dir in /usr/local/pgsql/include /usr/pgsql/include -dnl do -dnl CPPFLAGS="$save_CPPFLAGS -I$dir" -dnl AC_CHECK_HEADER([libpq-fe.h], [break]) -dnl CPPFLAGS="$save_CPPFLAGS" -dnl done -dnl AC_DEFINE(USE_SQL_PGSQL,1, -dnl [Define this if you are going to use PostgreSQL]) -dnl AC_DEFINE(HAVE_LIBPQ,1, -dnl [Define this if you have libpq]) -dnl PGSQLLIBS="$pm_cv_lib_pq" -dnl SQL_MODULES="$SQL_MODULES pgsqlmap.la" -dnl build_pgsqlmap=yes ], -dnl [ test "$build_pgsqlmap" = yes && AC_MSG_ERROR([cannot build the requested module pgsqlmap, because PostgreSQL libraries are not present]) -dnl build_pgsqlmap=no ], -dnl [/usr/pgsql/lib /usr/local/pgsql/lib])], $build_pgsqlmap) + build_mysql=yes ], + [ test "$build_mysql" = yes && AC_MSG_ERROR([MySQL support cannot be built because MySQL libraries are not present]) + build_mysql=no ], + [/usr/lib/mysql /usr/local/lib/mysql])], $build_mysql) + +GR_ENABLE(pgsql, [ + GR_CHECK_LIB(pq, PQconnectStart, [], + [ save_CPPFLAGS=$CPPFLAGS + for dir in /usr/local/pgsql/include /usr/pgsql/include + do + CPPFLAGS="$save_CPPFLAGS -I$dir" + AC_CHECK_HEADER([libpq-fe.h], [break]) + CPPFLAGS="$save_CPPFLAGS" + done + AC_DEFINE(USE_SQL_PGSQL,1, + [Define this if you are going to use PostgreSQL]) + AC_DEFINE(HAVE_LIBPQ,1, + [Define this if you have libpq]) + PGSQLLIBS="$pm_cv_lib_pq" + build_pgsql=yes ], + [ test "$build_pgsql" = yes && AC_MSG_ERROR([PostgreSQL support cannot be built because PostgreSQL libraries are not present]) + build_pgsql=no ], + [/usr/pgsql/lib /usr/local/pgsql/lib])], $build_pgsql) AC_SUBST(MYSQLLIBS) AC_SUBST(PGSQLLIBS) -AM_CONDITIONAL([USE_MYSQL],[test $build_mysqlmap = yes]) -AM_CONDITIONAL([USE_PGSQL],[test $build_pgsqlmap = yes]) +AM_CONDITIONAL([USE_MYSQL],[test $build_mysql = yes]) +AM_CONDITIONAL([USE_PGSQL],[test $build_pgsql = yes]) AC_CONFIG_COMMANDS([status],[ echo "" @@ -158,7 +155,7 @@ delim="-------------------------------------------------------------------" echo $delim | tr '-' '*' echo "Backends to build:" res= -for module in mysqlmap pgsqlmap +for module in mysql pgsql do eval enable=\${build_$module} str=`echo ${module}" yes" | sed 's/./-/g'` @@ -173,8 +170,8 @@ case "$res" in AC_MSG_ERROR([Nothing to build?]) esac ],[ -build_pgsqlmap=$build_pgsqlmap -build_mysqlmap=$build_mysqlmap]) +build_pgsql=$build_pgsql +build_mysql=$build_mysql]) dnl FIXME: tests/Makefile diff --git a/src/pgsql.c b/src/pgsql.c index e67e26c..3599b5f 100644 --- a/src/pgsql.c +++ b/src/pgsql.c @@ -1,7 +1,192 @@ -/* This file is a placeholder */ +/* This file is part of libvmod_dbrw + Copyright (C) 2013 Sergey Poznyakoff + + Libvmod_dbrw 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. + + Libvmod_dbrw 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 libvmod_dbrw. If not, see . +*/ #include "dbrw.h" +#include +#include + +#define ISSPACE(c) ((c) == ' ' || (c) == '\t') + +static char * +chop(char *str) +{ + int len; + + if (!str) + return NULL; + for (len = strlen(str); len > 0 && ISSPACE(str[len-1]); len--) + ; + str[len] = 0; + return str; +} + +struct vmod_pgsql_data +{ + PGconn *pgconn; + PGresult *res; +}; + +static int +s_pgsql_init(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = calloc (1, sizeof (*dp)); + if (!dp) + return ENOMEM; + conn->data = dp; + return 0; +} + +static void +s_pgsql_destroy(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + free(dp); + conn->data = NULL; +} + +static int +s_pgsql_connect(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + + dp->pgconn = PQsetdbLogin(findparam(conn->conf->param, "server"), + findparam(conn->conf->param, "port"), + findparam(conn->conf->param, "options"), + NULL, + findparam(conn->conf->param, "database"), + findparam(conn->conf->param, "user"), + findparam(conn->conf->param, "password")); + + if (PQstatus(dp->pgconn) == CONNECTION_BAD) { + dbrw_error("connection to the database failed: %s", + PQerrorMessage(dp->pgconn)); + PQfinish(dp->pgconn); + dp->pgconn = NULL; + return 1; + } + + return 0; +} + +static int +s_pgsql_disconnect(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + PQfinish(dp->pgconn); + return 0; +} + +static int +s_pgsql_query(struct dbrw_connection *conn, const char *query) +{ + struct vmod_pgsql_data *dp = conn->data; + ExecStatusType stat; + + dp->res = PQexec(dp->pgconn, query); + stat = PQresultStatus(dp->res); + + if (stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK) { + dbrw_error("query failed: %s", PQerrorMessage(dp->pgconn)); + dbrw_error("the failed query was: %s", query); + if (dp->res) { + PQclear(dp->res); + dp->res = NULL; + } + return 1; + } + + return 0; +} + +static int +s_pgsql_free_result(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + + PQclear(dp->res); + dp->res = NULL; + + return 0; +} + +static unsigned +s_pgsql_num_fields(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + if (!dp->res) + return 0; + return PQnfields(dp->res); +} + +static unsigned +s_pgsql_num_tuples(struct dbrw_connection *conn) +{ + struct vmod_pgsql_data *dp = conn->data; + if (!dp->res) + return 0; + return PQntuples(dp->res); +} + +static const char * +s_pgsql_get_column(struct dbrw_connection *conn, size_t nrow, size_t ncol) +{ + struct vmod_pgsql_data *dp = conn->data; + + if (!dp->res) + return NULL; + return chop(PQgetvalue(dp->res, nrow, ncol)); +} + +static char * +s_pgsql_escape(struct dbrw_connection *conn, const char *ustr) +{ + char *str, *q; + const unsigned char *p; + size_t len = strlen(ustr); +#define ESCAPABLE_CHAR "\\'\"" + + for (p = (const unsigned char *) ustr; *p; p++) { + if (strchr(ESCAPABLE_CHAR, *p)) + len++; + } + + str = malloc(len + 1); + if (!str) + return NULL; + + for (p = (const unsigned char *) ustr, q = str; *p; p++) { + if (strchr(ESCAPABLE_CHAR, *p)) + *q++ = '\\'; + *q++ = *p; + } + *q = 0; + return str; +} + -static struct dbrw_backend pgsql_backend = { +struct dbrw_backend pgsql_backend = { "pgsql", - /* FIXME */ + s_pgsql_init, + s_pgsql_destroy, + s_pgsql_connect, + s_pgsql_disconnect, + s_pgsql_escape, + s_pgsql_query, + s_pgsql_num_tuples, + s_pgsql_num_fields, + s_pgsql_free_result, + s_pgsql_get_column }; -- cgit v1.2.1