diff options
Diffstat (limited to 'gamma/gsql_conn.c')
-rw-r--r-- | gamma/gsql_conn.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/gamma/gsql_conn.c b/gamma/gsql_conn.c new file mode 100644 index 0000000..64cec27 --- /dev/null +++ b/gamma/gsql_conn.c @@ -0,0 +1,257 @@ +/* This file is part of Gamma. + Copyright (C) 2002, 2005, 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 <app.h> +#include <stdio.h> + +static int num_iface; +static struct sql_iface sql_iftab[MAX_IFACES]; + +SCM_GLOBAL_SYMBOL (gamma_sql_error, "sql-error"); + +static long sql_connect_tag = -1; + +/* SMOB functions: */ +static SCM +sql_connect_mark(SCM connect_smob) +{ + struct sql_connect *conn = (struct sql_connect *)SCM_CDR(connect_smob); + if (sql_iftab[conn->iface].mark) + return sql_iftab[conn->iface].mark(conn); +} + +static size_t +sql_connect_free(SCM connect_smob) +{ + struct sql_connect *conn = (struct sql_connect *)SCM_CDR(connect_smob); + sql_iftab[conn->iface].free(conn); + if (conn->hostname) + free(conn->hostname); + if (conn->username) + free(conn->username); + if (conn->database) + free(conn->database); + scm_gc_free(conn, sizeof *conn, "SQL connection"); + return 0; +} + +static int +sql_connect_print (SCM connect_smob, SCM port, scm_print_state * pstate) +{ + struct sql_connect *conn = (struct sql_connect *)SCM_CDR(connect_smob); + scm_puts("#<SQL connection (", port); + scm_puts(sql_iftab[conn->iface].name, port); + scm_puts(")", port); + if (!conn->data) + scm_puts("not connected", port); + else { + static const char *unspecified = "<unspecified>"; + scm_puts(conn->username ? conn->username : unspecified, port); + scm_puts("@", port); + scm_puts(conn->hostname ? conn->hostname : unspecified, port); + scm_puts(":", port); + scm_intprint(conn->port, 10, port); + scm_puts(" ", port); + scm_puts(conn->database ? conn->database : unspecified, port); + }; + scm_puts (">", port); + return 1; +} + +static int +sql_find_iface(const char *name) +{ + int iface; + + for (iface = 0; iface < num_iface; iface++) + if (strcmp(sql_iftab[iface].name, name) == 0) + return iface; + return -1; +} + +SCM +sql_connect_create(char *name) +{ + struct sql_connect *conn; + int iface = sql_find_iface(name); + if (iface < 0) + scm_misc_error("sql_connect_create", + "Unknown SQL interface ~S", + scm_list_1(scm_from_locale_string(name))); + + conn = scm_gc_malloc(sizeof (*conn), "sql_connect"); + memset(conn, 0, sizeof *conn); + conn->iface = iface; + SCM_RETURN_NEWSMOB(sql_connect_tag, conn); +} + +static int +scm_is_sql_connect(SCM scm) +{ + return SCM_NIMP(scm) && SCM_CAR (scm) == (SCM) sql_connect_tag; +} + +/* Interface */ + +static void +gamma_cvt_iface(SCM inval, void *outval, const char *func_name) +{ + int iface; + + if (scm_is_integer(inval)) + iface = scm_to_int(inval); + else if (scm_is_string(inval)) { + char *s = scm_to_locale_string(inval); + iface = sql_find_iface(s); + free(s); + } else + SCM_ASSERT(0, inval, SCM_ARG1, func_name); + + if (iface < 0 || iface >= num_iface) + scm_misc_error(func_name, + "Argument ~S (~S) out of range", + scm_list_2(scm_from_int(1), + inval)); + *(int*)outval = iface; +} + + +SCM_DEFINE_PUBLIC(sql_open_connection, "sql-open-connection", 1, 0, 0, + (SCM param), +"Connect to a database. Take connection parameters from @var{param}, " +"which must be a list of conses. In each cons, the @samp{car} contains " +"a @dfn{key} identifying the parameter, and the @samp{cdr} supplies the " +"value for that parameter.\n" +"\n" +"Valid keys are:\n" +"@table @asis\n" +"@item #:iface\n" +"Defines the type of the @acronym{SQL} interface (either @samp{mysql} or " +"@samp{postgres}.\n" +"@item #:host\n" +"Server host name.\n" +"@item #:port\n" +"Port number on @samp{#:host}.\n" +"@item #:socket\n" +"Socket path, if the server is listening on a UNIX socket. Either\n" +"@samp{#:port} or @samp{#:socket} may be given, but not both.\n" +"@item #:user\n" +"Sets the @acronym{SQL} user name.\n" +"@item #:pass\n" +"Sets the password for @samp{#:user}\n" +"@item #:ssl-cert\n" +"Full pathname of the @acronym{SSL} certificate to use (MySQL only).\n" +"@item #:config-file\n" +"Read missing parameters from the specified file (MySQL only).\n" +"@item #:config-group\n" +"Read the specified group in the config file (MySQL only).\n" +"@end table\n") +#define FUNC_NAME s_sql_open_connection +{ + int iface; + + struct gamma_parmdcl dcltab[] = { + { "iface", &iface, gamma_cvt_iface }, + { NULL } + }; + + SCM smob; + + SCM_ASSERT(scm_is_pair(param), param, SCM_ARG1, FUNC_NAME); + gamma_parmlist_parse (param, dcltab, + GAMMA_PARMLIST_IGNORE_UNKNOWN, FUNC_NAME); + + smob = sql_iftab[iface].connect(param, FUNC_NAME); + + return smob; +} +#undef FUNC_NAME + +SCM_DEFINE_PUBLIC(sql_close_connection, "sql-close-connection", 1, 0, 0, + (SCM conn), + "Close connection to a database.") +#define FUNC_NAME s_sql_close_connection +{ + struct sql_connect *cp; + SCM_ASSERT(scm_is_sql_connect(conn), conn, SCM_ARG1, FUNC_NAME); + cp = (struct sql_connect *)SCM_CDR(conn); + sql_iftab[cp->iface].close(cp); + return SCM_UNSPECIFIED; +} +#undef FUNC_NAME + +SCM_DEFINE_PUBLIC(sql_query, "sql-query", 2, 0, 0, + (SCM conn, SCM query), +"Send the SQL query @var{query} to the server using connection @var{conn} \ +and return the result.\n\ +If @var{query} is a @samp{SELECT}, or similar query, returning tuples, the \ +return value is a list of rows. Each row is a list of values, one for each \ +column. All values are returned as strings. NULL values are returned as \ +@samp{#f}.\n\n\ +If @var{query} is an @samp{UPDATE} (or a similar query), @code{sql-query} \ +returns number of rows affected by the query.\n") +#define FUNC_NAME s_sql_query +{ + struct sql_connect *conn_ptr; + char *query_ptr; + SCM ret; + + SCM_ASSERT(scm_is_sql_connect(conn), conn, SCM_ARG1, FUNC_NAME); + SCM_ASSERT(scm_is_string(query), query, SCM_ARG2, FUNC_NAME); + conn_ptr = (struct sql_connect *)SCM_CDR(conn); + query_ptr = scm_to_locale_string(query); + ret = sql_iftab[conn_ptr->iface].query(conn_ptr, query_ptr); + free(query_ptr); + return ret; +} +#undef FUNC_NAME + +int +sql_register_iface(struct sql_iface *ifp) +{ + int n = sql_find_iface(ifp->name); + if (n != -1) + return n; + if (num_iface >= MAX_IFACES) + scm_misc_error("sql_register_iface", + "Too many ifaces registered", + SCM_EOL); + memcpy(&sql_iftab[num_iface], ifp, sizeof sql_iftab[0]); + sql_iftab[num_iface].name = strdup(ifp->name); + return num_iface++; +} + +void +gsql_conn_init() +{ + if (sql_connect_tag == -1) { + sql_connect_tag = scm_make_smob_type ("sql_connect", + sizeof (struct sql_connect)); + scm_set_smob_mark (sql_connect_tag, sql_connect_mark); + scm_set_smob_free (sql_connect_tag, sql_connect_free); + scm_set_smob_print (sql_connect_tag, sql_connect_print); + } +#ifndef SCM_MAGIC_SNARFER +# include <gsql_conn.x> +#endif +} + + |