Commit 665a8c22 665a8c2218738b13325e65e611e3cac5e198ff22 by Sergey Poznyakoff

General SQL support (based on GNU Radius sql driver ideas)

1 parent e0323da5
Makefile
Makefile.in
*.la
*.lo
.deps
.libs
.gdbinit
modlist.h
\ No newline at end of file
## This file is part of GNU Mailutils
## Copyright (C) 2004 Free Software Foundation, Inc.
##
## GNU Mailutils 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 2, or (at
## your option) any later version.
##
## This program 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 this program; if not, write to the Free Software
## Foundation, Inc.
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
noinst_LTLIBRARIES=@BUILD_SQL@
EXTRA_LTLIBRARIES=libsql.la mysql.la postgres.la
EXTRA_DIST=\
mysql.c\
postgres.c
DISTCLEANFILES = modlist.h
INCLUDES=-I${top_srcdir}/include -I${top_srcdir}/mailbox/include -I${top_srcdir}/lib -I${top_builddir}/include/mailutils/gnu @INTLINCS@
libsql_la_SOURCES=sql.c
libsql_la_LIBADD=@SQL_LTLIBOBJS@
libsql_la_DEPENDENCIES = @SQL_LTLIBOBJS@
moddir = $(pkgdatadir)/$(VERSION)/modules
mod_LTLIBRARIES = @SQL_LOADABLE_MODULES@
MODFLAGS=-module -avoid-version -no-undefined -rpath $(moddir) -Wl,-Bsymbolic
mysql_la_SOURCES = mysql.c
mysql_la_LIBADD = @MYSQLLIBS@
mysql_la_LDFLAGS = $(MODFLAGS)
postgres_la_SOURCES = postgres.c
postgres_la_LIBADD = @POSTGRESLIBS@
postgres_la_LDFLAGS = $(MODFLAGS)
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2004 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/mailutils.h>
#include <mailutils/sql.h>
#include <mysql/mysql.h>
#include <mysql/errmsg.h>
struct mu_mysql_data
{
MYSQL *mysql;
MYSQL_RES *result;
};
static int
do_mysql_query (mu_sql_connection_t conn, char *query)
{
int rc;
int i;
MYSQL *mysql;
for (i = 0; i < 10; i++)
{
mysql = ((struct mu_mysql_data*)conn->data)->mysql;
rc = mysql_query (mysql, query);
if (rc && mysql_errno (mysql) == CR_SERVER_GONE_ERROR)
{
/* Reconnect? */
mu_sql_disconnect (conn);
mu_sql_connect (conn);
continue;
}
break;
}
return rc;
}
/* ************************************************************************* */
/* Interface routines */
static int
init (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = calloc (1, sizeof (*mp));
if (!mp)
return ENOMEM;
conn->data = mp;
return 0;
}
static int
destroy (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
free (mp->mysql);
free (mp);
conn->data = NULL;
return 0;
}
static int
connect (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
char *host, *socket_name;
mp->mysql = malloc (sizeof(MYSQL));
if (!mp->mysql)
return ENOMEM;
mysql_init (mp->mysql);
if (conn->server[0] == '/')
{
host = "localhost";
socket_name = conn->server;
}
else
host = conn->server;
if (!mysql_real_connect(mp->mysql,
host,
conn->login,
conn->password,
conn->dbname,
conn->port,
socket_name,
0))
return MU_ERR_SQL;
return 0;
}
static int
disconnect (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
mysql_close (mp->mysql);
free (mp->mysql);
mp->mysql = NULL;
return 0;
}
static int
query (mu_sql_connection_t conn, char *query)
{
if (do_mysql_query (conn, query))
return MU_ERR_SQL;
return 0;
}
static int
store_result (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
if (!(mp->result = mysql_store_result (mp->mysql)))
{
if (mysql_errno (mp->mysql))
return MU_ERR_SQL;
return MU_ERR_NO_RESULT;
}
return 0;
}
static int
release_result (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
mysql_free_result (mp->result);
return 0;
}
static int
num_columns (mu_sql_connection_t conn, size_t *np)
{
struct mu_mysql_data *mp = conn->data;
*np = mysql_num_fields (mp->result);
return 0;
}
static int
num_tuples (mu_sql_connection_t conn, size_t *np)
{
struct mu_mysql_data *mp = conn->data;
*np = mysql_num_rows (mp->result);
return 0;
}
static int
get_column (mu_sql_connection_t conn, size_t nrow, size_t ncol, char **pdata)
{
struct mu_mysql_data *mp = conn->data;
MYSQL_ROW row;
if (nrow >= mysql_num_rows (mp->result)
|| ncol >= mysql_num_fields (mp->result))
return MU_ERR_BAD_COLUMN;
mysql_data_seek (mp->result, nrow);
row = mysql_fetch_row (mp->result);
if (!row)
return MU_ERR_BAD_COLUMN;
*pdata = row[ncol];
return 0;
}
static char *
errstr (mu_sql_connection_t conn)
{
struct mu_mysql_data *mp = conn->data;
return mysql_error (mp->mysql);
}
MU_DECL_SQL_DISPATCH_T(mysql) = {
"mysql",
3306,
init,
destroy,
connect,
disconnect,
query,
store_result,
release_result,
num_tuples,
num_columns,
get_column,
errstr,
};
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2004 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_PGSQL
#include <mailutils/mailutils.h>
#include <mailutils/sql.h>
#include <libpq-fe.h>
#include <ctype.h>
static char *
chop (char *str)
{
int len;
for (len = strlen(str); len > 0 && isspace(str[len-1]); len--)
;
str[len] = 0;
return str;
}
struct mu_pgsql_data
{
PGconn *pgconn;
PGresult *res;
};
static int
init (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = calloc (1, sizeof (*dp));
if (!dp)
return ENOMEM;
conn->data = dp;
return 0;
}
static int
destroy (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = conn->data;
free (dp->pgconn);
free (dp);
conn->data = NULL;
return 0;
}
static int
connect (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = conn->data;
char portbuf[16];
char *portstr;
if (conn->port == 0)
portstr = NULL;
else
{
portstr = portbuf;
snprintf (portbuf, sizeof (portbuf), "%d", conn->port);
}
dp->pgconn = PQsetdbLogin (conn->server, portstr, NULL, NULL,
conn->dbname, conn->login, conn->password);
if (PQstatus (dp->pgconn) == CONNECTION_BAD)
return MU_ERR_SQL;
return 0;
}
static int
disconnect (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = conn->data;
PQfinish (dp->pgconn);
return 0;
}
static int
query (mu_sql_connection_t conn, char *query)
{
struct mu_pgsql_data *dp = conn->data;
ExecStatusType stat;
dp->res = PQexec (dp->pgconn, query);
if (dp->res == NULL)
return MU_ERR_SQL;
stat = PQresultStatus (dp->res);
if (stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK)
return MU_ERR_SQL;
return 0;
}
static int
store_result (mu_sql_connection_t conn)
{
return 0;
}
static int
release_result (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = conn->data;
PQclear (dp->res);
return 0;
}
static int
num_columns (mu_sql_connection_t conn, size_t *np)
{
struct mu_pgsql_data *dp = conn->data;
if (!dp->res)
return MU_ERR_NO_RESULT;
*np = PQnfields (dp->res);
return 0;
}
static int
num_tuples (mu_sql_connection_t conn, size_t *np)
{
struct mu_pgsql_data *dp = conn->data;
if (!dp->res)
return MU_ERR_NO_RESULT;
*np = PQntuples (dp->res);
return 0;
}
static int
get_column (mu_sql_connection_t conn, size_t nrow, size_t ncol, char **pdata)
{
struct mu_pgsql_data *dp = conn->data;
if (!dp->res)
return MU_ERR_NO_RESULT;
*pdata = chop (PQgetvalue (dp->res, nrow, ncol));
return 0;
}
static char *
errstr (mu_sql_connection_t conn)
{
struct mu_pgsql_data *dp = conn->data;
return PQerrorMessage (dp->pgconn);
}
MU_DECL_SQL_DISPATCH_T(postgres) = {
"postgres",
5432,
init,
destroy,
connect,
disconnect,
query,
store_result,
release_result,
num_tuples,
num_columns,
get_column,
errstr,
};
#endif
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2004 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/mailutils.h>
#include <mailutils/sql.h>
#include "modlist.h"
#define NSTATIC_MODS sizeof(static_dispatch_tab)/sizeof(static_dispatch_tab[0])
static mu_sql_dispatch_t **sql_disptab;
size_t sql_disptab_next;
size_t sql_disptab_size;
static int
init_disptab ()
{
if (!sql_disptab)
{
size_t size;
sql_disptab_size = NSTATIC_MODS;
size = sql_disptab_size * sizeof sql_disptab[0];
sql_disptab = malloc (size);
if (!sql_disptab)
return ENOMEM;
memcpy(sql_disptab, static_dispatch_tab, size);
sql_disptab_next = sql_disptab_size;
}
return 0;
}
static int
add_disptab (mu_sql_dispatch_t *tab)
{
if (sql_disptab_next == sql_disptab_size)
{
mu_sql_dispatch_t **tmp;
tmp = realloc (sql_disptab, sql_disptab_size + 4);
if (!tmp)
return ENOMEM;
sql_disptab = tmp;
sql_disptab_size += 4;
}
sql_disptab[sql_disptab_next] = tab;
return sql_disptab_next++;
}
int
mu_sql_interface_index (char *name)
{
int i;
//mu_sql_dispatch_t *tab;
init_disptab ();
for (i = 1; i < sql_disptab_next; i++)
if (sql_disptab[i] && (!name || strcmp (sql_disptab[i]->name, name) == 0))
return i;
// FIXME
// if (name && mu_sql_load_ext (name, "dispatch_tab", &tab))
// return add_disptab (tab);
return 0;
}
static mu_sql_dispatch_t *
get_sql_entry (int type)
{
init_disptab ();
if (type == 0 && sql_disptab[0] == NULL)
{
int i;
for (i = 1; i < sql_disptab_next; i++)
if (sql_disptab[i])
{
sql_disptab[type] = sql_disptab[i];
break;
}
}
if (!sql_disptab[type])
{
mu_error (_("SQL dispatcher table empty"));
abort ();
}
return sql_disptab[type];
}
#define SQL_F(c,f) (*get_sql_entry ((c)->interface) -> f)
int
mu_sql_connection_init (mu_sql_connection_t *pconn, int interface,
char *server, int port, char *login,
char *password, char *dbname)
{
static mu_sql_dispatch_t *tab;
mu_sql_connection_t conn;
tab = get_sql_entry (interface);
if (!tab)
return MU_ERR_NO_INTERFACE;
conn = calloc (1, sizeof (*conn));
if (!conn)
return ENOMEM;
conn->state = mu_sql_not_connected;
conn->interface = interface;
conn->server = server;
conn->port = port;
conn->login = login;
conn->password = password;
conn->dbname = dbname;
if (tab->init)
{
int rc = tab->init (conn);
if (rc)
{
free (conn);
return rc;
}
}
*pconn = conn;
return 0;
}
int
mu_sql_connection_destroy (mu_sql_connection_t *conn)
{
if (!conn || !*conn)
return EINVAL;
mu_sql_disconnect (*conn);
SQL_F (*conn, destroy) (*conn);
free (*conn);
*conn = NULL;
return 0;
}
int
mu_sql_connect (mu_sql_connection_t conn)
{
int rc;
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
break;
case mu_sql_connected:
case mu_sql_query_run:
return MU_ERR_DB_ALREADY_CONNECTED;
case mu_sql_result_available:
return MU_ERR_RESULT_NOT_RELEASED;
}
rc = SQL_F (conn, connect) (conn);
if (!rc)
conn->state = mu_sql_connected;
return rc;
}
int
mu_sql_disconnect (mu_sql_connection_t conn)
{
int rc;
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return 0;
case mu_sql_connected:
case mu_sql_query_run:
break;
case mu_sql_result_available:
return MU_ERR_RESULT_NOT_RELEASED;
}
rc = SQL_F (conn, disconnect) (conn);
if (rc == 0)
conn->state = mu_sql_not_connected;
return rc;
}
int
mu_sql_query (mu_sql_connection_t conn, char *query)
{
int rc;
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
case mu_sql_query_run:
break;
case mu_sql_result_available:
return MU_ERR_RESULT_NOT_RELEASED;
}
rc = SQL_F (conn, query) (conn, query);
if (rc == 0)
conn->state = mu_sql_query_run;
return rc;
}
int
mu_sql_store_result (mu_sql_connection_t conn)
{
int rc;
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
return MU_ERR_NO_QUERY;
case mu_sql_query_run:
break;
case mu_sql_result_available:
return MU_ERR_RESULT_NOT_RELEASED;
}
rc = SQL_F (conn, store_result) (conn);
if (rc == 0)
conn->state = mu_sql_result_available;
return rc;
}
int
mu_sql_release_result (mu_sql_connection_t conn)
{
int rc;
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
return MU_ERR_NO_QUERY;
case mu_sql_query_run:
return MU_ERR_NO_RESULT;
case mu_sql_result_available:
break;
}
rc = SQL_F (conn, release_result) (conn);
if (rc == 0)
conn->state = mu_sql_connected;
return rc;
}
int
mu_sql_num_tuples (mu_sql_connection_t conn, size_t *np)
{
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
return MU_ERR_NO_QUERY;
case mu_sql_query_run:
return MU_ERR_NO_RESULT;
case mu_sql_result_available:
break;
}
return SQL_F (conn, num_tuples) (conn, np);
}
int
mu_sql_num_columns (mu_sql_connection_t conn, size_t *np)
{
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
return MU_ERR_NO_QUERY;
case mu_sql_query_run:
return MU_ERR_NO_RESULT;
case mu_sql_result_available:
break;
}
return SQL_F (conn, num_columns) (conn, np);
}
int
mu_sql_get_column (mu_sql_connection_t conn, size_t nrow, size_t ncol,
char **pdata)
{
if (!conn)
return EINVAL;
switch (conn->state)
{
case mu_sql_not_connected:
return MU_ERR_DB_NOT_CONNECTED;
case mu_sql_connected:
return MU_ERR_NO_QUERY;
case mu_sql_query_run:
return MU_ERR_NO_RESULT;
case mu_sql_result_available:
break;
}
return SQL_F (conn, get_column) (conn, nrow, ncol, pdata);
}
char *
mu_sql_strerror (mu_sql_connection_t conn)
{
if (!conn)
return strerror (EINVAL);
return SQL_F (conn, errstr) (conn);
}