diff options
Diffstat (limited to 'gamma/pgsql.c')
-rw-r--r-- | gamma/pgsql.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/gamma/pgsql.c b/gamma/pgsql.c new file mode 100644 index 0000000..1421c40 --- /dev/null +++ b/gamma/pgsql.c @@ -0,0 +1,180 @@ +/* This file is part of Gamma. + Copyright (C) 2002, 2010 Sergey Poznyakoff + + Gamma 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. + + Gamma 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 Gamma. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include <guile-sql.h> +#include <libpq-fe.h> + +static size_t +s_pgsql_free(struct sql_connect *conn) +{ + PGconn *pgconn = (PGconn*) conn->data; + if (pgconn) + PQfinish(pgconn); + return 0; +} + +static SCM +s_pgsql_connect(SCM parmlist, const char *func_name) +{ + char *hostname = NULL; + char *port = NULL; + char *dbname = NULL; + char *user = NULL; + char *pass = NULL; + struct gamma_parmdcl dcltab[] = { + { "iface", NULL, NULL }, + { "host", &hostname, gamma_cvt_string }, + { "port", &port, gamma_cvt_string }, + { "db", &dbname, gamma_cvt_string }, + { "user", &user, gamma_cvt_string }, + { "pass", &pass, gamma_cvt_string }, + { NULL } + }; + + PGconn *pgconn; + SCM smob; + struct sql_connect *conn; + + gamma_parmlist_parse (parmlist, dcltab, 0, func_name); + + pgconn = PQsetdbLogin(hostname, port, NULL, NULL, dbname, user, pass); + + if (PQstatus(pgconn) == CONNECTION_BAD) { + SCM args, pmsg; + + free(hostname); + free(port); + free(user); + free(pass); + + pmsg = scm_from_locale_string(PQerrorMessage(pgconn)); + args = scm_list_1(scm_from_locale_string("Cannot connect to the database")); + PQfinish(pgconn); + scm_error(gamma_sql_error, func_name, + "~A", + args, + scm_list_2(SCM_BOOL_F, pmsg)); + } + + smob = sql_connect_create("pgsql"); + conn = (struct sql_connect *)SCM_CDR(smob); + conn->hostname = hostname; + conn->port = port ? atoi (port) : 0; + conn->username = user; + conn->database = dbname; + conn->data = pgconn; + return smob; +} + +static SCM +result_to_list(PGresult *res) +{ + int i, j; + int ntuples = PQntuples(res); + int nfields = PQnfields(res); + SCM row_head = SCM_EOL, row_tail = SCM_EOL; + + for (i = 0; i < ntuples; i++) { + SCM new_row; + SCM head = SCM_EOL, tail; + + for (j = 0; j < nfields; j++) { + char *val = PQgetvalue(res, i, j); + SCM new_elt = scm_cons(val ? + scm_from_locale_string(val) : + SCM_BOOL_F, SCM_EOL); + if (head == SCM_EOL) + head = new_elt; + else + SCM_SETCDR(tail, new_elt); + tail = new_elt; + } + + new_row = scm_cons(head, SCM_EOL); + + if (row_head == SCM_EOL) + row_head = new_row; + else + SCM_SETCDR(row_tail, new_row); + row_tail = new_row; + } + return row_head; +} + +static SCM +s_pgsql_query(struct sql_connect *conn, const char *query) +{ + PGconn *pgconn = (PGconn*) conn->data; + PGresult *res; + SCM cell = SCM_EOL; + ExecStatusType stat; + + res = PQexec(pgconn, query); + if (!res) + scm_error(gamma_sql_error, "sql-query", + "~A", + scm_list_1(scm_from_locale_string("Error executing PostgreSQL query")), + scm_list_2(SCM_BOOL_F, + scm_from_locale_string(PQerrorMessage(pgconn)))); + + stat = PQresultStatus(res); + + switch (stat) { + case PGRES_COMMAND_OK: + /* Successful completion of a command returning no data */ + cell = scm_from_ulong(strtoul(PQcmdTuples(res), NULL, 0)); + break; + case PGRES_TUPLES_OK: + /* The query successfully executed */ + cell = result_to_list(res); + PQclear(res); + break; + default: + scm_error(gamma_sql_error, "sql-query", + "~A", + scm_list_1(scm_from_locale_string("PostgreSQL error")), + scm_list_2(scm_from_uint(stat), + scm_from_locale_string(PQresStatus(stat)))); + } + + return cell; +} + +static void +s_pgsql_close(struct sql_connect *conn) +{ + PGconn *pgconn = (PGconn*) conn->data; + if (pgconn) { + PQfinish(pgconn); + conn->data = NULL; + } +} + +struct sql_iface pgsql_iface = { + "pgsql", + NULL, /* mark */ + s_pgsql_free, + s_pgsql_connect, + s_pgsql_close, + s_pgsql_query, +}; + + + |