/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program 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 of the License, 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 * * mod_sql_postgres.c: Module for POSTGRES SQL bindings */ /** * This module implements the POSTGRES database driver. * * Load this module (and the [[sql:]] module) and pass "postgres" as backend * driver name to [[sql:sql_connect()]]; * * The string describing the database connection (the 2nd argument to * [[sql:sql_connect()]]) is similar to the PQconnectdb conninfo string. * A detailed description of the format of this string can be found at: * * http://www.postgresql.org/docs/8.0/interactive/libpq.html#AEN22513 * * Example given: * * var db = sql_connect("postgres", "host=dbserver user=postgres dbname=template1"); * * foreach[] tuple (sql(db, "show all")) * debug "${shift tuple} = ${shift tuple}"; * */ #include #include #include #include "spl.h" #include "mod_sql.h" #include "compat.h" extern void SPL_ABI(spl_mod_sql_postgres_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_sql_postgres_done)(struct spl_vm *vm, struct spl_module *mod); struct postgres_backend_data { PGconn *conn; }; static struct spl_node *sql_postgres_query_callback(struct spl_task *task, void *backend_data, const char *query) { struct postgres_backend_data *pbd = backend_data; PGresult *res; res = PQexec(pbd->conn, query); if (PQresultStatus(res) == PGRES_COMMAND_OK) { PQclear(res); return spl_get(0); } if (PQresultStatus(res) == PGRES_TUPLES_OK) { struct spl_node *result = spl_get(0); int fields = PQnfields(res); int tuples = PQntuples(res); for (int t = 0; t < tuples; t++) { struct spl_node *n = spl_get(0); for (int f = 0; f < fields; f++) { char *name = PQfname(res, f); char *value = PQgetvalue(res, t, f); char *name_base = strrchr(name, '.'); name_base = name_base ? name_base+1 : name; spl_create(task, n, name_base, value ? SPL_NEW_STRING_DUP(value) : 0, SPL_CREATE_LOCAL); } spl_create(task, result, NULL, n, SPL_CREATE_LOCAL); } PQclear(res); return result; } spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "POSTGRES: SQL Error on '%s': %s!\n", query, PQerrorMessage(pbd->conn))), NULL); PQclear(res); return 0; } static void sql_postgres_close_callback(struct spl_vm UNUSED(*vm), void *backend_data) { struct postgres_backend_data *pbd = backend_data; PQfinish(pbd->conn); free(pbd); } static void sql_postgres_open_callback(struct spl_task *task, struct spl_node *node, const char *data) { struct postgres_backend_data *pbd = malloc(sizeof(struct postgres_backend_data)); pbd->conn = PQconnectdb(data ? data : ""); if (PQstatus(pbd->conn) != CONNECTION_OK) { spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "POSTGRES: Can't open database %s: %s!\n", data, PQerrorMessage(pbd->conn))), NULL); PQfinish(pbd->conn); free(pbd); return; } struct sql_hnode_data *hnd = malloc(sizeof(struct sql_hnode_data)); hnd->backend_data = pbd; hnd->query_callback = sql_postgres_query_callback; hnd->close_callback = sql_postgres_close_callback; node->hnode_data = hnd; } void SPL_ABI(spl_mod_sql_postgres_init)(struct spl_vm *vm, struct spl_module UNUSED(*mod), int restore) { if (!restore) spl_module_load(vm, "sql", 0); sql_register_backend(vm, "postgres", sql_postgres_open_callback); } void SPL_ABI(spl_mod_sql_postgres_done)(struct spl_vm *vm, struct spl_module UNUSED(*mod)) { sql_unregister_backend(vm, "postgres"); }