
/****************************************************************************
 *
 * MODULE:       db.execute
 * AUTHOR(S):    Radim Blazek <radim.blazek gmail.com> (original contributor)
 *               Huidae Cho <grass4u gmail.com>
 *               Glynn Clements <glynn gclements.plus.com>
 *               Hamish Bowman <hamish_b yahoo.com>
 *               Markus Neteler <neteler itc.it>
 *               Stephan Holl
 *               Martin Landa <landa.martin gmail.com>
 * PURPOSE:      process one non-select sql statement
 * COPYRIGHT:    (C) 2002-2011 by the GRASS Development Team
 *
 *               This program is free software under the GNU General
 *               Public License (>=v2). Read the file COPYING that
 *               comes with GRASS for details.
 *
 *****************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grass/gis.h>
#include <grass/dbmi.h>
#include <grass/glocale.h>

struct
{
    const char *driver, *database, *schema, *sql, *input;
    int i;
} parms;

/* function prototypes */
static void parse_command_line(int, char **);
static int get_stmt(FILE *, dbString *);
static int stmt_is_empty(dbString *);
static void error_handler(void *);

int main(int argc, char **argv)
{
    dbString stmt;
    dbDriver *driver;
    dbHandle handle;
    int ret;
    FILE *fd;
    int error;

    error = 0;

    parse_command_line(argc, argv);

    /* read from file or stdin ? */
    if (parms.input && strcmp(parms.input, "-") != 0) {
	fd = fopen(parms.input, "r");
	if (fd == NULL) {
	    G_fatal_error(_("Unable to open file <%s>: %s"),
                          parms.input, strerror(errno));
	}
    }
    else {
	fd = stdin;
    }
    
    /* open DB connection */
    db_init_string(&stmt);
    
    driver = db_start_driver(parms.driver);
    if (driver == NULL) {
	G_fatal_error(_("Unable to start driver <%s>"), parms.driver);
    }

    db_init_handle(&handle);
    db_set_handle(&handle, parms.database, parms.schema);
    if (db_open_database(driver, &handle) != DB_OK)
	G_fatal_error(_("Unable to open database <%s>"), parms.database);
    G_add_error_handler(error_handler, driver);
    
    if (parms.sql) {
        /* parms.sql */
        db_set_string(&stmt, parms.sql);
        ret = db_execute_immediate(driver, &stmt);
    }
    else { /* parms.input */
        while (get_stmt(fd, &stmt)) {
            if (stmt_is_empty(&stmt))
                continue;
            G_debug(3, "sql: %s", db_get_string(&stmt));
                
            ret = db_execute_immediate(driver, &stmt);
            
            if (ret != DB_OK) {
                if (parms.i) {	/* ignore SQL errors */
                    G_warning(_("Error while executing: '%s'"),
                              db_get_string(&stmt));
                    error++;
                }
                else {
                    G_fatal_error(_("Error while executing: '%s'"),
                                  db_get_string(&stmt));
                }
            }
        }
    }
    
    db_close_database(driver);
    db_shutdown_driver(driver);

    exit(error ? EXIT_FAILURE : EXIT_SUCCESS);
}


static void parse_command_line(int argc, char **argv)
{
    struct Option *driver, *database, *schema, *sql, *input;
    struct Flag *i;
    struct GModule *module;
    const char *drv, *db, *schema_name;

    /* Initialize the GIS calls */
    G_gisinit(argv[0]);

    /* Set description */
    module = G_define_module();
    G_add_keyword(_("database"));
    G_add_keyword(_("attribute table"));
    G_add_keyword(_("SQL"));
    module->label = _("Executes any SQL statement.");
    module->description = _("For SELECT statements use 'db.select'.");

    sql = G_define_standard_option(G_OPT_DB_SQL);
    sql->label = _("SQL statement");
    sql->description = _("Example: update rybniky set kapri = 'hodne' where kapri = 'malo'");
    sql->guisection = _("SQL");
    
    input = G_define_standard_option(G_OPT_F_INPUT);
    input->required = NO;
    input->label = _("Name of file containing SQL statement(s)");
    input->description = _("'-' for standard input");
    input->guisection = _("SQL");

    driver = G_define_standard_option(G_OPT_DB_DRIVER);
    driver->options = db_list_drivers();
    driver->guisection = _("Connection");
    if ((drv = db_get_default_driver_name()))
	driver->answer = (char *) drv;
    
    database = G_define_standard_option(G_OPT_DB_DATABASE);
    database->guisection = _("Connection");
    if ((db = db_get_default_database_name()))
	database->answer = (char *) db;

    schema = G_define_standard_option(G_OPT_DB_SCHEMA);
    schema->guisection = _("Connection");
    if ((schema_name = db_get_default_schema_name()))
	schema->answer = (char *) schema_name;

    i = G_define_flag();
    i->key = 'i';
    i->description = _("Ignore SQL errors and continue");
    i->guisection = _("Errors");
    
    if (G_parser(argc, argv))
	exit(EXIT_SUCCESS);

    if (!sql->answer && !input->answer) {
        G_fatal_error(_("You must provide <%s> or <%s> option"),
                      sql->key, input->key);
    }
    
    parms.driver = driver->answer;
    parms.database = database->answer;
    parms.schema = schema->answer;
    parms.sql = sql->answer;
    parms.input = input->answer;
    parms.i = i->answer ? TRUE : FALSE;
}

int get_stmt(FILE * fd, dbString * stmt)
{
    char buf[DB_SQL_MAX], buf2[DB_SQL_MAX];
    size_t len;

    db_zero_string(stmt);

    if (G_getl2(buf, sizeof(buf), fd) == 0)
        return 0;
    
    strcpy(buf2, buf);
    G_chop(buf2);
    len = strlen(buf2);
        
    if (buf2[len - 1] == ';') { /* end of statement */
        buf2[len - 1] = 0;      /* truncate ';' */
    }
    
    db_set_string(stmt, buf2);

    return 1;
}

int stmt_is_empty(dbString * stmt)
{
    char dummy[2];

    return (sscanf(db_get_string(stmt), "%1s", dummy) != 1);
}

void error_handler(void *p)
{
    dbDriver *driver = (dbDriver *) p;
    
    db_close_database(driver);
    db_shutdown_driver(driver);
}
