From 6c4cf9a9f784287438832b92a8ce84cd2c6e70f6 Mon Sep 17 00:00:00 2001 From: Stefan Reddig Date: Fri, 6 Nov 2009 21:42:05 +0100 Subject: [PATCH] Add commit from Stefan Reddig --- bacula/src/cats/Makefile.in | 21 +- bacula/src/cats/cats.h | 112 +++- bacula/src/cats/create_bacula_database.in | 5 +- bacula/src/cats/create_ingres_database.in | 29 + bacula/src/cats/drop_bacula_database.in | 5 +- bacula/src/cats/drop_bacula_tables.in | 6 +- bacula/src/cats/drop_ingres_database.in | 15 + bacula/src/cats/drop_ingres_tables.in | 40 ++ bacula/src/cats/grant_bacula_privileges.in | 3 + bacula/src/cats/grant_ingres_privileges.in | 63 ++ bacula/src/cats/ingres.c | 664 +++++++++++++++++++++ bacula/src/cats/ingres.in | 8 + bacula/src/cats/make_bacula_tables.in | 5 +- bacula/src/cats/make_ingres_tables.in | 416 +++++++++++++ bacula/src/cats/myingres.c | 489 +++++++++++++++ bacula/src/cats/myingres.h | 72 +++ bacula/src/cats/myingres.sc | 509 ++++++++++++++++ bacula/src/cats/myingres.sh | 83 +++ bacula/src/cats/sql.c | 42 +- bacula/src/cats/sql_create.c | 6 +- bacula/src/cats/sql_delete.c | 6 +- bacula/src/cats/sql_find.c | 4 +- bacula/src/cats/sql_get.c | 5 +- bacula/src/cats/sql_list.c | 6 +- bacula/src/cats/sql_update.c | 6 +- bacula/src/cats/update_bacula_tables.in | 6 +- bacula/src/cats/update_ingres_tables.in | 12 + 27 files changed, 2607 insertions(+), 31 deletions(-) create mode 100755 bacula/src/cats/create_ingres_database.in create mode 100755 bacula/src/cats/drop_ingres_database.in create mode 100755 bacula/src/cats/drop_ingres_tables.in create mode 100755 bacula/src/cats/grant_ingres_privileges.in create mode 100755 bacula/src/cats/ingres.c create mode 100755 bacula/src/cats/ingres.in create mode 100755 bacula/src/cats/make_ingres_tables.in create mode 100644 bacula/src/cats/myingres.c create mode 100644 bacula/src/cats/myingres.h create mode 100644 bacula/src/cats/myingres.sc create mode 100644 bacula/src/cats/myingres.sh create mode 100755 bacula/src/cats/update_ingres_tables.in diff --git a/bacula/src/cats/Makefile.in b/bacula/src/cats/Makefile.in index e39266f687..7afabe084e 100644 --- a/bacula/src/cats/Makefile.in +++ b/bacula/src/cats/Makefile.in @@ -27,9 +27,10 @@ dummy: INCLUDE_FILES = bdb.h cats.h protos.h sql_cmds.h LIBBACSQL_SRCS = mysql.c bdb.c dbi.c \ - sql.c sql_cmds.c sql_create.c sql_delete.c sql_find.c \ - sql_get.c sql_list.c sql_update.c sqlite.c \ - postgresql.c bvfs.c + sql.c sql_cmds.c sql_create.c sql_delete.c sql_find.c \ + sql_get.c sql_list.c sql_update.c sqlite.c \ + postgresql.c ingres.c myingres.c \ + bvfs.c LIBBACSQL_OBJS = $(LIBBACSQL_SRCS:.c=.o) LIBBACSQL_LOBJS = $(LIBBACSQL_SRCS:.c=.lo) @@ -49,11 +50,17 @@ LIBBACSQL_LT_AGE = 0 .c.lo: @echo "Compiling $<" $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(SQL_INC) $(DINCLUDE) $(CFLAGS) $< + #------------------------------------------------------------------------- all: Makefile libbacsql$(DEFAULT_ARCHIVE_TYPE) @echo "==== Make of sqllib is good ====" @echo " " +# SRE: embeddedSQL precompiler run +esql: + $(II_SYSTEM)/ingres/bin/esqlc -omyingres.c myingres.sc + $(II_SYSTEM)/ingres/bin/esqlc -omyingres.h myingres.sh + libbacsql.a: $(LIBBACSQL_OBJS) @echo "Making $@ ..." $(AR) rc $@ $(LIBBACSQL_OBJS) @@ -90,13 +97,16 @@ realclean: clean $(RMF) create_postgresql_database update_postgresql_tables make_postgresql_tables $(RMF) grant_postgresql_privileges drop_postgresql_tables drop_postgresql_database + $(RMF) create_ingres_database update_ingres_tables make_ingres_tables + $(RMF) grant_ingres_privileges drop_ingres_tables drop_ingres_database + $(RMF) create_sqlite_database update_sqlite_tables make_sqlite_tables $(RMF) grant_sqlite_privileges drop_sqlite_tables drop_sqlite_database $(RMF) create_sqlite3_database update_sqlite3_tables make_sqlite3_tables $(RMF) grant_sqlite3_privileges drop_sqlite3_tables drop_sqlite3_database - $(RMF) mysql sqlite postgresql + $(RMF) mysql sqlite postgresql ingres $(RMF) make_catalog_backup delete_catalog_backup distclean: realclean @@ -182,7 +192,8 @@ uninstall: @LIBTOOL_UNINSTALL_TARGET@ @INCLUDE_UNINSTALL_TARGET@ # and it also includes system headers. # `semi'-automatic since dependencies are generated at distribution time. -depend: +#depend: esql <- SRE: if generating from 'real' ingres source +depend: @$(MV) Makefile Makefile.bak @$(SED) "/^# DO NOT DELETE:/,$$ d" Makefile.bak > Makefile @$(ECHO) "# DO NOT DELETE: nice dependency list follows" >> Makefile diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index 8c0d4f90c4..94b6532abe 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -38,7 +38,7 @@ * for the external world. This is control with * the define __SQL_C, which is defined only in sql.c * - * Version $Id$ + * Version $Id: cats.h 8478 2009-02-18 20:11:55Z kerns $ */ /* @@ -73,6 +73,7 @@ enum { SQL_TYPE_MYSQL = 0, SQL_TYPE_POSTGRESQL = 1, SQL_TYPE_SQLITE = 2, + SQL_TYPE_INGRES = 3, SQL_TYPE_SQLITE3 }; @@ -535,6 +536,114 @@ extern const char* my_pg_batch_fill_path_query; #else +#ifdef HAVE_INGRES + +#include "myingres.h" + +#define BDB_VERSION 11 + +/* TEMP: the following is taken from select OID, typname from pg_type; */ /*SRE: huh? */ +#define IS_NUM(x) ((x) == 20 || (x) == 21 || (x) == 23 || (x) == 700 || (x) == 701) +#define IS_NOT_NULL(x) ((x) == 1) + +typedef char **INGRES_ROW; + +/* + * This is the "real" definition that should only be + * used inside sql.c and associated database interface + * subroutines. + * + * I N G R E S + */ +struct B_DB { + BQUEUE bq; /* queue control */ + brwlock_t lock; /* transaction lock */ + INGconn *db; + INGresult *result; + int status; + INGRES_ROW row; + INGRES_FIELD *fields; + int num_rows; + int row_size; /* size of malloced rows */ + int num_fields; + int fields_size; /* size of malloced fields */ + int row_number; /* row number from my_ingres_data_seek */ + int field_number; /* field number from my_ingres_field_seek */ + int ref_count; + char *db_name; + char *db_user; + char *db_password; + char *db_address; /* host address */ + char *db_socket; /* socket for local access */ + int db_port; /* port of host address */ + int have_insert_id; /* do have insert_id() */ + bool connected; + POOLMEM *errmsg; /* nicely edited error message */ + POOLMEM *cmd; /* SQL command string */ + POOLMEM *cached_path; + int cached_path_len; /* length of cached path */ + uint32_t cached_path_id; + bool allow_transactions; /* transactions allowed */ + bool transaction; /* transaction started */ + int changes; /* changes made to db */ + POOLMEM *fname; /* Filename only */ + POOLMEM *path; /* Path only */ + POOLMEM *esc_name; /* Escaped file name */ + POOLMEM *esc_path; /* Escaped path name */ + int fnl; /* file name length */ + int pnl; /* path name length */ +}; + +void my_ingres_free_result(B_DB *mdb); +INGRES_ROW my_ingres_fetch_row (B_DB *mdb); +int my_ingres_query (B_DB *mdb, const char *query); +void my_ingres_data_seek (B_DB *mdb, int row); +int my_ingres_currval (B_DB *mdb, const char *table_name); +void my_ingres_field_seek (B_DB *mdb, int row); +INGRES_FIELD * my_ingres_fetch_field(B_DB *mdb); +void my_ingres_close (B_DB *mdb); + +int my_ingres_batch_start(JCR *jcr, B_DB *mdb); +int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error); +typedef struct ATTR_DBR ATTR_DBR; +int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar); +char *my_ingres_copy_escape(char *dest, char *src, size_t len); + +extern const char* my_ingres_batch_lock_path_query; +extern const char* my_ingres_batch_lock_filename_query; +extern const char* my_ingres_batch_unlock_tables_query; +extern const char* my_ingres_batch_fill_filename_query; +extern const char* my_ingres_batch_fill_path_query; + +/* "Generic" names for easier conversion */ +#define sql_store_result(x) ((x)->result) +#define sql_free_result(x) my_ingres_free_result(x) +#define sql_fetch_row(x) my_ingres_fetch_row(x) +#define sql_query(x, y) my_ingres_query((x), (y)) +#define sql_close(x) my_ingres_close(x) +#define sql_strerror(x) INGerrorMessage((x)->db) +#define sql_num_rows(x) ((unsigned) INGntuples((x)->result)) +#define sql_data_seek(x, i) my_ingres_data_seek((x), (i)) +#define sql_affected_rows(x) ((unsigned) atoi(INGcmdTuples((x)->result))) +#define sql_insert_id(x,y) my_ingres_currval((x), (y)) +#define sql_field_seek(x, y) my_ingres_field_seek((x), (y)) +#define sql_fetch_field(x) my_ingres_fetch_field(x) +#define sql_num_fields(x) ((x)->num_fields) + +#define sql_batch_start(x,y) my_ingres_batch_start(x,y) +#define sql_batch_end(x,y,z) my_ingres_batch_end(x,y,z) +#define sql_batch_insert(x,y,z) my_ingres_batch_insert(x,y,z) +#define sql_batch_lock_path_query my_ingres_batch_lock_path_query +#define sql_batch_lock_filename_query my_ingres_batch_lock_filename_query +#define sql_batch_unlock_tables_query my_ingres_batch_unlock_tables_query +#define sql_batch_fill_filename_query my_ingres_batch_fill_filename_query +#define sql_batch_fill_path_query my_ingres_batch_fill_path_query + +#define SQL_ROW INGRES_ROW +#define SQL_FIELD INGRES_FIELD + +#else + #ifdef HAVE_DBI #define BDB_VERSION 11 @@ -719,6 +828,7 @@ struct B_DB { #endif /* HAVE_MYSQL */ #endif /* HAVE_SQLITE */ #endif /* HAVE_POSTGRESQL */ +#endif /* HAVE_INGRES */ #endif /* HAVE_DBI */ #endif diff --git a/bacula/src/cats/create_bacula_database.in b/bacula/src/cats/create_bacula_database.in index 4db10bfe7d..16008698ba 100644 --- a/bacula/src/cats/create_bacula_database.in +++ b/bacula/src/cats/create_bacula_database.in @@ -1,7 +1,7 @@ #!/bin/sh # # This routine creates the Bacula database -# using PostgreSQL, MySQL, or SQLite. +# using PostgreSQL, Ingres, MySQL, or SQLite. # if test xsqlite3 = x@DB_TYPE@ ; then echo "Creating SQLite database" @@ -10,6 +10,9 @@ else if test xmysql = x@DB_TYPE@ ; then echo "Creating MySQL database" @scriptdir@/create_mysql_database $* + elif test xingres = x@DB_TYPE@ ; then + echo "Creating Ingres database" + @scriptdir@/create_ingres_database $* else echo "Creating PostgreSQL database" @scriptdir@/create_postgresql_database $* diff --git a/bacula/src/cats/create_ingres_database.in b/bacula/src/cats/create_ingres_database.in new file mode 100755 index 0000000000..a287352f86 --- /dev/null +++ b/bacula/src/cats/create_ingres_database.in @@ -0,0 +1,29 @@ +#!/bin/sh +# +# shell script to create Bacula database(s) +# + +bindir=@SQL_BINDIR@ +db_name=@db_name@ + +# use SQL_ASCII to be able to put any filename into +# the database even those created with unusual character sets +ENCODING="ENCODING 'SQL_ASCII'" + +# use UTF8 if you are using standard Unix/Linux LANG specifications +# that use UTF8 -- this is normally the default and *should* be +# your standard. Bacula works correctly *only* with correct UTF8. +# +# Note, with this encoding, if you have any "weird" filenames on +# your system (names generated from Win32 or Mac OS), you may +# get Bacula batch insert failures. +# +#ENCODING="ENCODING 'UTF8'" + +if createdb $* ${db_name} +then + echo "Creation of ${db_name} database succeeded." +else + echo "Creation of ${db_name} database failed." +fi +exit 0 diff --git a/bacula/src/cats/drop_bacula_database.in b/bacula/src/cats/drop_bacula_database.in index bd1dbf158c..d542af0c3a 100755 --- a/bacula/src/cats/drop_bacula_database.in +++ b/bacula/src/cats/drop_bacula_database.in @@ -1,7 +1,7 @@ #!/bin/sh # # Drop Bacula database -- works for whatever is configured, -# MySQL, SQLite, PostgreSQL +# MySQL, SQLite, PostgreSQL, Ingres # if test xsqlite3 = x@DB_TYPE@ ; then @scriptdir@/drop_@DB_TYPE@_database $* @@ -9,6 +9,9 @@ else if test xmysql = x@DB_TYPE@ ; then echo "Making MySQL database" @scriptdir@/drop_mysql_database $* + elif test xingres = x@DB_TYPE@ ; then + echo "Dropping Ingres database" + @scriptdir@/drop_ingres_database $* else @scriptdir@/drop_postgresql_database $* fi diff --git a/bacula/src/cats/drop_bacula_tables.in b/bacula/src/cats/drop_bacula_tables.in index 97509289ca..459d504661 100755 --- a/bacula/src/cats/drop_bacula_tables.in +++ b/bacula/src/cats/drop_bacula_tables.in @@ -1,7 +1,7 @@ #!/bin/sh # # Drop Bacula tables -- works for whatever is configured, -# MySQL, SQLite, or PostgreSQL +# MySQL, SQLite, Ingres, or PostgreSQL # if test xsqlite3 = x@DB_TYPE@ ; then @scriptdir@/drop_@DB_TYPE@_tables $* @@ -11,6 +11,10 @@ else echo "Making MySQL tables" @scriptdir@/drop_mysql_tables $* echo "Dropped MySQL tables" + elif test xingres = x@DB_TYPE@ ; then + echo "Dropping Ingres tables" + @scriptdir@/drop_ingres_tables $* + echo "Dropped Ingres tables" else # hardcoded database name - should be a parameter @scriptdir@/drop_postgresql_tables $* diff --git a/bacula/src/cats/drop_ingres_database.in b/bacula/src/cats/drop_ingres_database.in new file mode 100755 index 0000000000..23c75fe287 --- /dev/null +++ b/bacula/src/cats/drop_ingres_database.in @@ -0,0 +1,15 @@ +#!/bin/sh +# +# shell script to drop Bacula database(s) +# + +bindir=@SQL_BINDIR@ +db_name=@db_name@ + +if destroydb ${db_name} +then + echo "Drop of ${db_name} database succeeded." +else + echo "Drop of ${db_name} database failed." +fi +exit 0 diff --git a/bacula/src/cats/drop_ingres_tables.in b/bacula/src/cats/drop_ingres_tables.in new file mode 100755 index 0000000000..ad7f23caa8 --- /dev/null +++ b/bacula/src/cats/drop_ingres_tables.in @@ -0,0 +1,40 @@ +#!/bin/sh +# +# shell script to delete Bacula tables for PostgreSQL + +bindir=@SQL_BINDIR@ +db_name=@db_name@ + +sql -u${db_user} ${db_name} $* <bq)); ) { + if (bstrcmp(mdb->db_name, db_name) && + bstrcmp(mdb->db_address, db_address) && + mdb->db_port == db_port) { + Dmsg2(100, "DB REopen %d %s\n", mdb->ref_count, db_name); + mdb->ref_count++; + V(mutex); + return mdb; /* already open */ + } + } + } + Dmsg0(100, "db_open first time\n"); + mdb = (B_DB *)malloc(sizeof(B_DB)); + memset(mdb, 0, sizeof(B_DB)); + mdb->db_name = bstrdup(db_name); + mdb->db_user = bstrdup(db_user); + if (db_password) { + mdb->db_password = bstrdup(db_password); + } + if (db_address) { + mdb->db_address = bstrdup(db_address); + } + if (db_socket) { + mdb->db_socket = bstrdup(db_socket); + } + mdb->db_port = db_port; + mdb->have_insert_id = TRUE; + mdb->errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */ + *mdb->errmsg = 0; + mdb->cmd = get_pool_memory(PM_EMSG); /* get command buffer */ + mdb->cached_path = get_pool_memory(PM_FNAME); + mdb->cached_path_id = 0; + mdb->ref_count = 1; + mdb->fname = get_pool_memory(PM_FNAME); + mdb->path = get_pool_memory(PM_FNAME); + mdb->esc_name = get_pool_memory(PM_FNAME); + mdb->esc_path = get_pool_memory(PM_FNAME); + mdb->allow_transactions = mult_db_connections; + qinsert(&db_list, &mdb->bq); /* put db in list */ + V(mutex); + return mdb; +} + +/* Check that the database correspond to the encoding we want */ +static bool check_database_encoding(JCR *jcr, B_DB *mdb) +{ +/* SRE: TODO! Needed? + SQL_ROW row; + int ret=false; + + if (!db_sql_query(mdb, "SELECT getdatabaseencoding()", NULL, NULL)) { + Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg); + return false; + } + + if ((row = sql_fetch_row(mdb)) == NULL) { + Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb)); + Jmsg(jcr, M_ERROR, 0, "Can't check database encoding %s", mdb->errmsg); + } else { + ret = bstrcmp(row[0], "SQL_ASCII"); + if (!ret) { + Mmsg(mdb->errmsg, + _("Encoding error for database \"%s\". Wanted SQL_ASCII, got %s\n"), + mdb->db_name, row[0]); + Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg); + Dmsg1(50, "%s", mdb->errmsg); + } + } + return ret; +*/ + return true; +} + +/* + * Check for errors in DBMS work + */ +static int sql_check(B_DB *mdb) +{ + return INGcheck(); +} + +/* + * Now actually open the database. This can generate errors, + * which are returned in the errmsg + * + * DO NOT close the database or free(mdb) here !!!! + */ +int +db_open_database(JCR *jcr, B_DB *mdb) +{ + int errstat; + char buf[10], *port; + + P(mutex); + if (mdb->connected) { + V(mutex); + return 1; + } + mdb->connected = false; + + if ((errstat=rwl_init(&mdb->lock)) != 0) { + berrno be; + Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"), + be.bstrerror(errstat)); + V(mutex); + return 0; + } + + if (mdb->db_port) { + bsnprintf(buf, sizeof(buf), "%d", mdb->db_port); + port = buf; + } else { + port = NULL; + } + + mdb->db = INGconnectDB(mdb->db_name, mdb->db_user, mdb->db_password); + + Dmsg0(50, "Ingres real CONNECT done\n"); + Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->db_user, mdb->db_name, + mdb->db_password==NULL?"(NULL)":mdb->db_password); + + if (sql_check(mdb)) { + Mmsg2(&mdb->errmsg, _("Unable to connect to Ingres server.\n" + "Database=%s User=%s\n" + "It is probably not running or your password is incorrect.\n"), + mdb->db_name, mdb->db_user); + V(mutex); + return 0; + } + + mdb->connected = true; + + if (!check_tables_version(jcr, mdb)) { + V(mutex); + return 0; + } + + //sql_query(mdb, "SET datestyle TO 'ISO, YMD'"); + + /* check that encoding is SQL_ASCII */ + check_database_encoding(jcr, mdb); + + V(mutex); + return 1; +} + +void +db_close_database(JCR *jcr, B_DB *mdb) +{ + if (!mdb) { + return; + } + db_end_transaction(jcr, mdb); + P(mutex); + sql_free_result(mdb); + mdb->ref_count--; + if (mdb->ref_count == 0) { + qdchain(&mdb->bq); + if (mdb->connected && mdb->db) { + sql_close(mdb); + } + rwl_destroy(&mdb->lock); + free_pool_memory(mdb->errmsg); + free_pool_memory(mdb->cmd); + free_pool_memory(mdb->cached_path); + free_pool_memory(mdb->fname); + free_pool_memory(mdb->path); + free_pool_memory(mdb->esc_name); + free_pool_memory(mdb->esc_path); + if (mdb->db_name) { + free(mdb->db_name); + } + if (mdb->db_user) { + free(mdb->db_user); + } + if (mdb->db_password) { + free(mdb->db_password); + } + if (mdb->db_address) { + free(mdb->db_address); + } + if (mdb->db_socket) { + free(mdb->db_socket); + } + free(mdb); + } + V(mutex); +} + +void db_thread_cleanup() +{ } + +/* + * Return the next unique index (auto-increment) for + * the given table. Return NULL on error. + * + * For Ingres, NULL causes the auto-increment value SRE: true? + * to be updated. + */ +int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index) +{ + strcpy(index, "NULL"); + return 1; +} + + +/* + * Escape strings so that Ingres is happy + * + * NOTE! len is the length of the old string. Your new + * string must be long enough (max 2*old+1) to hold + * the escaped output. + * SRE: TODO! + */ +void +db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len) +{ +/* + int error; + + PQescapeStringConn(mdb->db, snew, old, len, &error); + if (error) { + Jmsg(jcr, M_FATAL, 0, _("PQescapeStringConn returned non-zero.\n"));*/ + /* error on encoding, probably invalid multibyte encoding in the source string + see PQescapeStringConn documentation for details. */ +/* Dmsg0(500, "PQescapeStringConn failed\n"); + }*/ +} + +/* + * Submit a general SQL command (cmd), and for each row returned, + * the sqlite_handler is called with the ctx. + */ +bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx) +{ + SQL_ROW row; + + Dmsg0(500, "db_sql_query started\n"); + + db_lock(mdb); + if (sql_query(mdb, query) != 0) { + Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb)); + db_unlock(mdb); + Dmsg0(500, "db_sql_query failed\n"); + return false; + } + Dmsg0(500, "db_sql_query succeeded. checking handler\n"); + + if (result_handler != NULL) { + Dmsg0(500, "db_sql_query invoking handler\n"); + if ((mdb->result = sql_store_result(mdb)) != NULL) { + int num_fields = sql_num_fields(mdb); + + Dmsg0(500, "db_sql_query sql_store_result suceeded\n"); + while ((row = sql_fetch_row(mdb)) != NULL) { + + Dmsg0(500, "db_sql_query sql_fetch_row worked\n"); + if (result_handler(ctx, num_fields, row)) + break; + } + + sql_free_result(mdb); + } + } + db_unlock(mdb); + + Dmsg0(500, "db_sql_query finished\n"); + + return true; +} + +/* + * Close database connection + */ +void my_ingres_close(B_DB *mdb) +{ + INGdisconnectDB(mdb->db); + //SRE: error handling? +} + +INGRES_ROW my_ingres_fetch_row(B_DB *mdb) +{ + int j; + INGRES_ROW row = NULL; // by default, return NULL + + Dmsg0(500, "my_ingres_fetch_row start\n"); + + if (!mdb->row || mdb->row_size < mdb->num_fields) { + int num_fields = mdb->num_fields; + Dmsg1(500, "we have need space of %d bytes\n", sizeof(char *) * mdb->num_fields); + + if (mdb->row) { + Dmsg0(500, "my_ingres_fetch_row freeing space\n"); + free(mdb->row); + } + num_fields += 20; /* add a bit extra */ + mdb->row = (INGRES_ROW)malloc(sizeof(char *) * num_fields); + mdb->row_size = num_fields; + + // now reset the row_number now that we have the space allocated + mdb->row_number = 0; + } + + // if still within the result set + if (mdb->row_number < mdb->num_rows) { + Dmsg2(500, "my_ingres_fetch_row row number '%d' is acceptable (0..%d)\n", mdb->row_number, mdb->num_rows); + // get each value from this row + for (j = 0; j < mdb->num_fields; j++) { + mdb->row[j] = INGgetvalue(mdb->result, mdb->row_number, j); + Dmsg2(500, "my_ingres_fetch_row field '%d' has value '%s'\n", j, mdb->row[j]); + } + // increment the row number for the next call + mdb->row_number++; + + row = mdb->row; + } else { + Dmsg2(500, "my_ingres_fetch_row row number '%d' is NOT acceptable (0..%d)\n", mdb->row_number, mdb->num_rows); + } + + Dmsg1(500, "my_ingres_fetch_row finishes returning %p\n", row); + + return row; +} + + +int my_ingres_max_length(B_DB *mdb, int field_num) { + // + // for a given column, find the max length + // + int max_length; + int i; + int this_length; + + max_length = 0; + for (i = 0; i < mdb->num_rows; i++) { + if (INGgetisnull(mdb->result, i, field_num)) { + this_length = 4; // "NULL" + } else { + this_length = cstrlen(INGgetvalue(mdb->result, i, field_num)); + } + + if (max_length < this_length) { + max_length = this_length; + } + } + + return max_length; +} + +INGRES_FIELD * my_ingres_fetch_field(B_DB *mdb) +{ + int i; + + Dmsg0(500, "my_ingres_fetch_field starts\n"); + + if (!mdb->fields || mdb->fields_size < mdb->num_fields) { + if (mdb->fields) { + free(mdb->fields); + } + Dmsg1(500, "allocating space for %d fields\n", mdb->num_fields); + mdb->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * mdb->num_fields); + mdb->fields_size = mdb->num_fields; + + for (i = 0; i < mdb->num_fields; i++) { + Dmsg1(500, "filling field %d\n", i); + strcpy(mdb->fields[i].name,INGfname(mdb->result, i)); + mdb->fields[i].max_length = my_ingres_max_length(mdb, i); + mdb->fields[i].type = INGftype(mdb->result, i); + mdb->fields[i].flags = 0; + + Dmsg4(500, "my_ingres_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n", + mdb->fields[i].name, mdb->fields[i].max_length, mdb->fields[i].type, + mdb->fields[i].flags); + } // end for + } // end if + + // increment field number for the next time around + + Dmsg0(500, "my_ingres_fetch_field finishes\n"); + return &mdb->fields[mdb->field_number++]; +} + +void my_ingres_data_seek(B_DB *mdb, int row) +{ + // set the row number to be returned on the next call + // to my_ingres_fetch_row + mdb->row_number = row; +} + +void my_ingres_field_seek(B_DB *mdb, int field) +{ + mdb->field_number = field; +} + +/* + * Note, if this routine returns 1 (failure), Bacula expects + * that no result has been stored. + * This is where QUERY_DB comes with Ingres. SRE: true? + * + * Returns: 0 on success + * 1 on failure + * + */ +int my_ingres_query(B_DB *mdb, const char *query) +{ + Dmsg0(500, "my_ingres_query started\n"); + // We are starting a new query. reset everything. + mdb->num_rows = -1; + mdb->row_number = -1; + mdb->field_number = -1; + + if (mdb->result) { + INGclear(mdb->result); /* hmm, someone forgot to free?? */ + mdb->result = NULL; + } + + Dmsg1(500, "my_ingres_query starts with '%s'\n", query); + mdb->result = INGexec(mdb->db, query); + if (!mdb->result) { + Dmsg1(50, "Query failed: %s\n", query); + goto bail_out; + } + + mdb->status = INGresultStatus(mdb->result); + if (mdb->status == ING_COMMAND_OK) { + Dmsg1(500, "we have a result\n", query); + + // how many fields in the set? + mdb->num_fields = (int)INGnfields(mdb->result); + Dmsg1(500, "we have %d fields\n", mdb->num_fields); + + mdb->num_rows = INGntuples(mdb->result); + Dmsg1(500, "we have %d rows\n", mdb->num_rows); + + mdb->status = 0; /* succeed */ + } else { + Dmsg1(50, "Result status failed: %s\n", query); + goto bail_out; + } + + Dmsg0(500, "my_ingres_query finishing\n"); + return mdb->status; + +bail_out: + Dmsg1(500, "we failed\n", query); + INGclear(mdb->result); + mdb->result = NULL; + mdb->status = 1; /* failed */ + return mdb->status; +} + +void my_ingres_free_result(B_DB *mdb) +{ + + db_lock(mdb); + if (mdb->result) { + INGclear(mdb->result); + mdb->result = NULL; + } + + if (mdb->row) { + free(mdb->row); + mdb->row = NULL; + } + + if (mdb->fields) { + free(mdb->fields); + mdb->fields = NULL; + } + db_unlock(mdb); +} + +int my_ingres_currval(B_DB *mdb, const char *table_name) +{ + // TODO! + return -1; +} + +#ifdef HAVE_BATCH_FILE_INSERT + +int my_ingres_batch_start(JCR *jcr, B_DB *mdb) +{ + //TODO! + return ING_ERROR; +} + +/* set error to something to abort operation */ +int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error) +{ + //TODO! + return ING_ERROR; +} + +int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) +{ + //TODO! + return ING_ERROR; +} + +#endif /* HAVE_BATCH_FILE_INSERT */ + +/* + * Escape strings so that Ingres is happy on COPY + * + * NOTE! len is the length of the old string. Your new + * string must be long enough (max 2*old+1) to hold + * the escaped output. + */ +char *my_ingres_copy_escape(char *dest, char *src, size_t len) +{ + /* we have to escape \t, \n, \r, \ */ + char c = '\0' ; + + while (len > 0 && *src) { + switch (*src) { + case '\n': + c = 'n'; + break; + case '\\': + c = '\\'; + break; + case '\t': + c = 't'; + break; + case '\r': + c = 'r'; + break; + default: + c = '\0' ; + } + + if (c) { + *dest = '\\'; + dest++; + *dest = c; + } else { + *dest = *src; + } + + len--; + src++; + dest++; + } + + *dest = '\0'; + return dest; +} + +#ifdef HAVE_BATCH_FILE_INSERT +const char *my_ingres_batch_lock_path_query = + "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE"; + + +const char *my_ingres_batch_lock_filename_query = + "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE"; + +const char *my_ingres_batch_unlock_tables_query = "COMMIT"; + +const char *my_ingres_batch_fill_path_query = + "INSERT INTO Path (Path) " + "SELECT a.Path FROM " + "(SELECT DISTINCT Path FROM batch) AS a " + "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) "; + + +const char *my_ingres_batch_fill_filename_query = + "INSERT INTO Filename (Name) " + "SELECT a.Name FROM " + "(SELECT DISTINCT Name FROM batch) as a " + "WHERE NOT EXISTS " + "(SELECT Name FROM Filename WHERE Name = a.Name)"; +#endif /* HAVE_BATCH_FILE_INSERT */ + +#endif /* HAVE_INGRES */ diff --git a/bacula/src/cats/ingres.in b/bacula/src/cats/ingres.in new file mode 100755 index 0000000000..a3adde664b --- /dev/null +++ b/bacula/src/cats/ingres.in @@ -0,0 +1,8 @@ +#!/bin/sh +# +# shell script to create Bacula PostgreSQL tables +# +bindir=@SQL_BINDIR@ +db_name=@db_name@ + +sql $* ${db_name} diff --git a/bacula/src/cats/make_bacula_tables.in b/bacula/src/cats/make_bacula_tables.in index c8469f12bf..6d9221d1e9 100755 --- a/bacula/src/cats/make_bacula_tables.in +++ b/bacula/src/cats/make_bacula_tables.in @@ -1,7 +1,7 @@ #!/bin/sh # # This routine makes the appropriately configured -# Bacula tables for PostgreSQL, MySQL, or SQLite. +# Bacula tables for PostgreSQL, Ingres, MySQL, or SQLite. # if test xsqlite3 = x@DB_TYPE@ ; then echo "Making SQLite tables" @@ -10,6 +10,9 @@ else if test xmysql = x@DB_TYPE@ ; then echo "Making MySQL tables" @scriptdir@/make_mysql_tables $* + elif test xingres = x@DB_TYPE@ ; then + echo "Making Ingres tables" + @scriptdir@/make_ingres_tables $* else echo "Making PostgreSQL tables" @scriptdir@/make_postgresql_tables $* diff --git a/bacula/src/cats/make_ingres_tables.in b/bacula/src/cats/make_ingres_tables.in new file mode 100755 index 0000000000..04a6ec22e0 --- /dev/null +++ b/bacula/src/cats/make_ingres_tables.in @@ -0,0 +1,416 @@ +#!/bin/sh +# +# shell script to create Bacula PostgreSQL tables +# +bindir=@SQL_BINDIR@ +db_name=${db_name:-@db_name@} + +sql $* ${db_name} < +#include +#include +#include "myingres.h" +#define INGRES_DEBUG 0 +#define DEBB(x) if (INGRES_DEBUG >= x) { +#define DEBE } +/* ---Implementations--- */ +int INGcheck() +{ + char errbuf[256]; + if (sqlca.sqlcode < 0) + { +/* # line 23 "myingres.sc" */ /* inquire_ingres */ + { + IILQisInqSqlio((short *)0,1,32,255,errbuf,63); + } +/* # line 24 "myingres.sc" */ /* host code */ + printf("Ingres-DBMS-Fehler: %s\n", errbuf); + return sqlca.sqlcode; + } + else + return 0; +} +short INGgetCols(const char *stmt) +{ + short number = 1; + IISQLDA *sqlda; + sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)); + if (sqlda == (IISQLDA *)0) + { printf("Failure allocating %d SQLDA elements\n",number); } + sqlda->sqln = number; + char stmt_buffer[2000]; + strcpy(stmt_buffer,stmt); +/* # line 46 "myingres.sc" */ /* prepare */ + { + IIsqInit(&sqlca); + IIsqPrepare(0,(char *)"s1",(char *)0,0,stmt_buffer); + } +/* # line 47 "myingres.sc" */ /* describe */ + { + IIsqInit(&sqlca); + IIsqDescribe(0,(char *)"s1",sqlda,0); + } +/* # line 49 "myingres.sc" */ /* host code */ + number = sqlda->sqld; + free(sqlda); + return number; +} +IISQLDA *INGgetDescriptor(short numCols, const char *stmt) +{ + IISQLDA *sqlda; + sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)); + if (sqlda == (IISQLDA *)0) + { printf("Failure allocating %d SQLDA elements\n",numCols); } + sqlda->sqln = numCols; + char stmt_buffer[2000]; + strcpy(stmt_buffer,stmt); +/* # line 69 "myingres.sc" */ /* prepare */ + { + IIsqInit(&sqlca); + IIsqPrepare(0,(char *)"s2",sqlda,0,stmt_buffer); + } +/* # line 71 "myingres.sc" */ /* host code */ + int i; + for (i=0;isqld;++i) + { + sqlda->sqlvar[i].sqldata = + (char *)malloc(sqlda->sqlvar[i].sqllen); + if (sqlda->sqlvar[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",sqlda->sqlvar[i].sqllen); } + sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short)); + if (sqlda->sqlvar[i].sqlind == (short *)0) + { printf("Failure allocating sqlind\n"); } + } + return sqlda; +} +void INGfreeDescriptor(IISQLDA *sqlda) +{ + int i; + for ( i = 0 ; i < sqlda->sqld ; ++i ) + { + free(sqlda->sqlvar[i].sqldata); + free(sqlda->sqlvar[i].sqlind); + } + free(sqlda); + sqlda = NULL; +} +int INGgetTypeSize(IISQLVAR *ingvar) +{ + int inglength = 0; + switch (ingvar->sqltype) + { + case IISQ_DTE_TYPE: + inglength = 25; + break; + case IISQ_MNY_TYPE: + inglength = 8; + break; + default: + inglength = ingvar->sqllen; + } + return inglength; +} +INGresult *INGgetINGresult(IISQLDA *sqlda) +{ + INGresult *result = NULL; + result = (INGresult *)calloc(1, sizeof(INGresult)); + if (result == (INGresult *)0) + { printf("Failure allocating INGresult\n"); } + result->sqlda = sqlda; + result->num_fields = sqlda->sqld; + result->num_rows = 0; + result->first_row = NULL; + result->status = ING_EMPTY_RESULT; + result->act_row = NULL; + strcpy(result->numrowstring,""); + result->fields = (INGRES_FIELD *)calloc(1, sizeof(INGRES_FIELD) * result->num_fields); + if (result->fields == (INGRES_FIELD *)0) + { printf("Failure allocating %d INGRES_FIELD elements\n",result->num_fields); } + DEBB(2) + printf("INGgetINGresult, before loop over %d fields\n", result->num_fields); + DEBE + int i; + for (i=0;inum_fields;++i) + { + memset(result->fields[i].name,'\0',34); + strncpy(result->fields[i].name, + sqlda->sqlvar[i].sqlname.sqlnamec, + sqlda->sqlvar[i].sqlname.sqlnamel); + result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]); + result->fields[i].type = abs(sqlda->sqlvar[i].sqltype); + result->fields[i].flags = (abs(sqlda->sqlvar[i].sqltype)<0) ? 1 : 0; + } + return result; +} +void INGfreeINGresult(INGresult *ing_res) +{ + /* TODO: free all rows and fields, then res, not descriptor! */ + if( ing_res != NULL ) + { + /* use of rows is a nasty workaround til I find the reason, + why aggregates like max() don't work + */ + int rows = ing_res->num_rows; + ING_ROW *rowtemp; + ing_res->act_row = ing_res->first_row; + while (ing_res->act_row != NULL && rows > 0) + { + rowtemp = ing_res->act_row->next; + INGfreeRowSpace(ing_res->act_row, ing_res->sqlda); + ing_res->act_row = rowtemp; + --rows; + } + free(ing_res->fields); + } + free(ing_res); + ing_res = NULL; +} +ING_ROW *INGgetRowSpace(INGresult *ing_res) +{ + IISQLDA *sqlda = ing_res->sqlda; + ING_ROW *row = NULL; + IISQLVAR *vars = NULL; + row = (ING_ROW *)calloc(1,sizeof(ING_ROW)); + if (row == (ING_ROW *)0) + { printf("Failure allocating ING_ROW\n"); } + vars = (IISQLVAR *)calloc(1,sizeof(IISQLVAR) * sqlda->sqld); + if (vars == (IISQLVAR *)0) + { printf("Failure allocating %d SQLVAR elements\n",sqlda->sqld); } + row->sqlvar = vars; + row->next = NULL; + int i; + unsigned short len; /* used for VARCHAR type length */ + for (i=0;isqld;++i) + { + /* make strings out of the data, then the space and assign + (why string? at least it seems that way, looking into the sources) + */ + switch (abs(ing_res->fields[i].type)) + { + case IISQ_VCH_TYPE: + len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len; + DEBB(2) + printf("length of varchar: %d\n", len); + DEBE + vars[i].sqldata = (char *)malloc(len+1); + if (vars[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",len+1); } + memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len); + vars[i].sqldata[len] = '\0'; + break; + case IISQ_CHA_TYPE: + vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1); + if (vars[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",ing_res->fields[i].max_length); } + memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen); + vars[i].sqldata[ing_res->fields[i].max_length] = '\0'; + break; + case IISQ_INT_TYPE: + vars[i].sqldata = (char *)malloc(20); + memset(vars[i].sqldata,'\0',20); + sprintf(vars[i].sqldata,"%d",*(int*)sqlda->sqlvar[i].sqldata); + break; + } + vars[i].sqlind = (short *)malloc(sizeof(short)); + if (sqlda->sqlvar[i].sqlind == (short *)0) + { printf("Failure allocating sqlind\n"); } + memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short)); + DEBB(2) + printf("INGgetRowSpace, Field %d, type %d, length %d, name %s\n", + i, sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqllen, ing_res->fields[i].name); + DEBE + } + return row; +} +void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda) +{ + int i; + if (row == NULL || sqlda == NULL) + { + printf("INGfreeRowSpace: one argument is NULL!\n"); + return; + } + for ( i = 0 ; i < sqlda->sqld ; ++i ) + { + free(row->sqlvar[i].sqldata); + free(row->sqlvar[i].sqlind); + } + free(row->sqlvar); + free(row); +} +int INGfetchAll(const char *stmt, INGresult *ing_res) +{ + int linecount = 0; + ING_ROW *row; + IISQLDA *desc; + char stmt_buffer[2000]; + strcpy(stmt_buffer,stmt); + desc = ing_res->sqlda; +/* # line 275 "myingres.sc" */ /* host code */ + INGcheck(); +/* # line 277 "myingres.sc" */ /* open */ + { + IIsqInit(&sqlca); + IIcsOpen((char *)"c2",19215,16475); + IIwritio(0,(short *)0,1,32,0,(char *)"s2"); + IIcsQuery((char *)"c2",19215,16475); + } +/* # line 278 "myingres.sc" */ /* host code */ + INGcheck(); + /* for (linecount=0;sqlca.sqlcode==0;++linecount) */ + while(sqlca.sqlcode==0) + { +/* # line 283 "myingres.sc" */ /* fetch */ + { + IIsqInit(&sqlca); + if (IIcsRetScroll((char *)"c2",19215,16475,-1,-1) != 0) { + IIcsDaGet(0,desc); + IIcsERetrieve(); + } /* IIcsRetrieve */ + } +/* # line 284 "myingres.sc" */ /* host code */ + INGcheck(); + if (sqlca.sqlcode == 0) + { + row = INGgetRowSpace(ing_res); /* alloc space for fetched row */ + /* initialize list when encountered first time */ + if (ing_res->first_row == 0) + { + ing_res->first_row = row; /* head of the list */ + ing_res->first_row->next = NULL; + ing_res->act_row = ing_res->first_row; + } + ing_res->act_row->next = row; /* append row to old act_row */ + ing_res->act_row = row; /* set row as act_row */ + row->row_number = linecount; + ++linecount; + DEBB(2) + int i; + printf("Row %d ", linecount); + for (i=0;inum_fields;++i) + { printf("F%d:%s ",i,row->sqlvar[i].sqldata); } + printf("\n"); + DEBE + } + } +/* # line 313 "myingres.sc" */ /* close */ + { + IIsqInit(&sqlca); + IIcsClose((char *)"c2",19215,16475); + } +/* # line 315 "myingres.sc" */ /* host code */ + ing_res->status = ING_COMMAND_OK; + ing_res->num_rows = linecount; + return linecount; +} +ING_STATUS INGresultStatus(INGresult *res) +{ + if (res == NULL) {return ING_NO_RESULT;} + return res->status; +} +void INGrowSeek(INGresult *res, int row_number) +{ + if (res->act_row->row_number == row_number) { return; } + /* TODO: real error handling */ + if (row_number<0 || row_number>res->num_rows) { return;} + ING_ROW *trow = res->first_row; + while ( trow->row_number != row_number ) + { trow = trow->next; } + res->act_row = trow; + /* note - can be null - if row_number not found, right? */ +} +char *INGgetvalue(INGresult *res, int row_number, int column_number) +{ + if (row_number != res->act_row->row_number) + { INGrowSeek(res, row_number); } + return res->act_row->sqlvar[column_number].sqldata; +} +int INGgetisnull(INGresult *res, int row_number, int column_number) +{ + if (row_number != res->act_row->row_number) + { INGrowSeek(res, row_number); } + return (short)*res->act_row->sqlvar[column_number].sqlind; +} +int INGntuples(const INGresult *res) +{ + return res->num_rows; +} +int INGnfields(const INGresult *res) +{ + return res->num_fields; +} +char *INGfname(const INGresult *res, int column_number) +{ + if ( (column_number > res->num_fields) || (column_number < 0) ) + { return NULL; } + else + { return res->fields[column_number].name; } +} +short INGftype(const INGresult *res, int column_number) +{ + return res->fields[column_number].type; +} +INGresult *INGexec(INGconn *conn, const char *query) +{ + /* TODO: error handling -> res->status? */ + IISQLDA *desc = NULL; + INGresult *res = NULL; + int cols = -1; + char stmt[2000]; + strncpy(stmt,query,strlen(query)); + stmt[strlen(query)]='\0'; + DEBB(1) + printf("INGexec: query is >>%s<<\n",stmt); + DEBE + if ((cols = INGgetCols(query)) == 0) + { + DEBB(1) + printf("INGexec: non-select\n"); + DEBE + /* non-select statement - TODO: EXECUTE IMMEDIATE */ +/* # line 400 "myingres.sc" */ /* execute */ + { + IIsqInit(&sqlca); + IIsqExImmed(stmt); + IIsyncup((char *)0,0); + } +/* # line 401 "myingres.sc" */ /* host code */ + } + else + { + DEBB(1) + printf("INGexec: select\n"); + DEBE + /* select statement */ + desc = INGgetDescriptor(cols, query); + res = INGgetINGresult(desc); + INGfetchAll(query, res); + } + return res; +} +void INGclear(INGresult *res) +{ + if (res == NULL) { return; } + IISQLDA *desc = res->sqlda; + INGfreeINGresult(res); + INGfreeDescriptor(desc); +} +INGconn *INGconnectDB(char *dbname, char *user, char *passwd) +{ + if (dbname == NULL || strlen(dbname) == 0) + { return NULL; } + INGconn *dbconn = (INGconn *)calloc(1,sizeof(INGconn)); + if (dbconn == (INGconn *)0) + { printf("Failure allocating INGconn\n"); } + char ingdbname[24]; + char ingdbuser[32]; + char ingdbpasw[32]; + char conn_name[32]; + int sess_id; + strcpy(ingdbname,dbname); + if ( user != NULL) + { + DEBB(1) + printf("Connection: with user/passwd\n"); + DEBE + strcpy(ingdbuser,user); + if ( passwd != NULL) + { strcpy(ingdbpasw,passwd); } + else + { strcpy(ingdbpasw, ""); } +/* # line 452 "myingres.sc" */ /* connect */ + { + IIsqInit(&sqlca); + IIsqUser(ingdbuser); + IIsqConnect(0,ingdbname,(char *)"-dbms_password",ingdbpasw,(char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0); + } +/* # line 456 "myingres.sc" */ /* host code */ + } + else + { + DEBB(1) + printf("Connection: w/ user/passwd\n"); + DEBE +/* # line 462 "myingres.sc" */ /* connect */ + { + IIsqInit(&sqlca); + IIsqConnect(0,ingdbname,(char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0); + } +/* # line 463 "myingres.sc" */ /* host code */ + } +/* # line 465 "myingres.sc" */ /* inquire_sql */ + { + IILQisInqSqlio((short *)0,1,32,31,conn_name,13); + } +/* # line 466 "myingres.sc" */ /* inquire_sql */ + { + IILQisInqSqlio((short *)0,1,30,sizeof(sess_id),&sess_id,11); + } +/* # line 468 "myingres.sc" */ /* host code */ + strcpy(dbconn->dbname,ingdbname); + strcpy(dbconn->user,ingdbuser); + strcpy(dbconn->password,ingdbpasw); + strcpy(dbconn->connection_name,conn_name); + dbconn->session_id = sess_id; + DEBB(1) + printf("Connected to '%s' with user/passwd %s/%s, sessID/name %i/%s\n", + dbconn->dbname, + dbconn->user, + dbconn->password, + dbconn->session_id, + dbconn->connection_name + ); + DEBE + return dbconn; +} +void INGdisconnectDB(INGconn *dbconn) +{ + /* TODO: use of dbconn */ +/* # line 490 "myingres.sc" */ /* disconnect */ + { + IIsqInit(&sqlca); + IIsqDisconnect(); + } +/* # line 491 "myingres.sc" */ /* host code */ + free(dbconn); +} +char *INGerrorMessage(const INGconn *conn) +{ + return NULL; +} +char *INGcmdTuples(INGresult *res) +{ + return res->numrowstring; +} +/* TODO? +char *INGerrorMessage(const INGconn *conn); +int INGputCopyEnd(INGconn *conn, const char *errormsg); +int INGputCopyData(INGconn *conn, const char *buffer, int nbytes); +*/ diff --git a/bacula/src/cats/myingres.h b/bacula/src/cats/myingres.h new file mode 100644 index 0000000000..7a8575e414 --- /dev/null +++ b/bacula/src/cats/myingres.h @@ -0,0 +1,72 @@ +# include "/opt/Ingres/IngresII/ingres/files/eqdefc.h" +#ifndef _MYINGRES_SH +#define _MYINGRES_SH +# include "/opt/Ingres/IngresII/ingres/files/eqsqlda.h" +/* ---typedefs--- */ +typedef struct ing_field { + char name[34]; + int max_length; + unsigned int type; + unsigned int flags; // 1 == not null +} INGRES_FIELD; +typedef struct ing_row { + IISQLVAR *sqlvar; /* ptr to sqlvar[sqld] for one row */ + struct ing_row *next; + int row_number; +} ING_ROW; +typedef enum ing_status { + ING_COMMAND_OK, + ING_TUPLES_OK, + ING_NO_RESULT, + ING_NO_ROWS_PROCESSED, + ING_EMPTY_RESULT, + ING_ERROR +} ING_STATUS; +typedef struct ing_varchar { + short len; + char* value; +} ING_VARCHAR; +/* It seems, Bacula needs the complete query result stored in one data structure */ +typedef struct ing_result { + IISQLDA *sqlda; /* descriptor */ + INGRES_FIELD *fields; + int num_rows; + int num_fields; + ING_STATUS status; + ING_ROW *first_row; + ING_ROW *act_row; /* just for iterating */ + char numrowstring[10]; +} INGresult; +typedef struct ing_conn { + char dbname[24]; + char user[32]; + char password[32]; + char connection_name[32]; + int session_id; +} INGconn; +/* ---Prototypes--- */ +int INGcheck(); +ING_STATUS INGresultStatus(INGresult *res); +short INGgetCols(const char *stmt); +IISQLDA *INGgetDescriptor(short numCols, const char *stmt); +void INGfreeDescriptor(IISQLDA *sqlda); +int INGgetTypeSize(IISQLVAR *ingvar); +INGresult *INGgetINGresult(IISQLDA *sqlda); +void INGfreeINGresult(INGresult *ing_res); +ING_ROW *INGgetRowSpace(INGresult *ing_res); +void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda); +int INGfetchAll(const char *stmt, INGresult *ing_res); +void INGrowSeek(INGresult *res, int row_number); +char *INGgetvalue(INGresult *res, int row_number, int column_number); +int INGgetisnull(INGresult *res, int row_number, int column_number); +int INGntuples(const INGresult *res); +int INGnfields(const INGresult *res); +char *INGfname(const INGresult *res, int column_number); +short INGftype(const INGresult *res, int column_number); +INGresult *INGexec(INGconn *db, const char *query); +void INGclear(INGresult *res); +INGconn *INGconnectDB(char *dbname, char *user, char *passwd); +void INGdisconnectDB(INGconn *dbconn); +char *INGerrorMessage(const INGconn *conn); +char *INGcmdTuples(INGresult *res); +#endif /* _MYINGRES_SH */ diff --git a/bacula/src/cats/myingres.sc b/bacula/src/cats/myingres.sc new file mode 100644 index 0000000000..89a25623f3 --- /dev/null +++ b/bacula/src/cats/myingres.sc @@ -0,0 +1,509 @@ +EXEC SQL INCLUDE SQLCA; +EXEC SQL INCLUDE SQLDA; + +#include +#include +#include + +#include "myingres.h" + +#define INGRES_DEBUG 0 +#define DEBB(x) if (INGRES_DEBUG >= x) { +#define DEBE } + +/* ---Implementations--- */ +int INGcheck() +{ + EXEC SQL BEGIN DECLARE SECTION; + char errbuf[256]; + EXEC SQL END DECLARE SECTION; + + if (sqlca.sqlcode < 0) + { + EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT); + printf("Ingres-DBMS-Fehler: %s\n", errbuf); + return sqlca.sqlcode; + } + else + return 0; +} + +short INGgetCols(const char *stmt) +{ + short number = 1; + IISQLDA *sqlda; + sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)); + if (sqlda == (IISQLDA *)0) + { printf("Failure allocating %d SQLDA elements\n",number); } + sqlda->sqln = number; + + EXEC SQL BEGIN DECLARE SECTION; + char stmt_buffer[2000]; + EXEC SQL END DECLARE SECTION; + + strcpy(stmt_buffer,stmt); + + EXEC SQL PREPARE s1 from :stmt_buffer; + EXEC SQL DESCRIBE s1 into :sqlda; + + number = sqlda->sqld; + free(sqlda); + return number; +} + +IISQLDA *INGgetDescriptor(short numCols, const char *stmt) +{ + IISQLDA *sqlda; + sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)); + if (sqlda == (IISQLDA *)0) + { printf("Failure allocating %d SQLDA elements\n",numCols); } + + sqlda->sqln = numCols; + + EXEC SQL BEGIN DECLARE SECTION; + char stmt_buffer[2000]; + EXEC SQL END DECLARE SECTION; + + strcpy(stmt_buffer,stmt); + + EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt_buffer; + + int i; + for (i=0;isqld;++i) + { + sqlda->sqlvar[i].sqldata = + (char *)malloc(sqlda->sqlvar[i].sqllen); + if (sqlda->sqlvar[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",sqlda->sqlvar[i].sqllen); } + + sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short)); + if (sqlda->sqlvar[i].sqlind == (short *)0) + { printf("Failure allocating sqlind\n"); } + } + + return sqlda; +} +void INGfreeDescriptor(IISQLDA *sqlda) +{ + int i; + for ( i = 0 ; i < sqlda->sqld ; ++i ) + { + free(sqlda->sqlvar[i].sqldata); + free(sqlda->sqlvar[i].sqlind); + } + free(sqlda); + sqlda = NULL; +} + +int INGgetTypeSize(IISQLVAR *ingvar) +{ + int inglength = 0; + + switch (ingvar->sqltype) + { + case IISQ_DTE_TYPE: + inglength = 25; + break; + case IISQ_MNY_TYPE: + inglength = 8; + break; + default: + inglength = ingvar->sqllen; + } + + return inglength; +} + +INGresult *INGgetINGresult(IISQLDA *sqlda) +{ + INGresult *result = NULL; + + result = (INGresult *)calloc(1, sizeof(INGresult)); + if (result == (INGresult *)0) + { printf("Failure allocating INGresult\n"); } + + result->sqlda = sqlda; + result->num_fields = sqlda->sqld; + result->num_rows = 0; + result->first_row = NULL; + result->status = ING_EMPTY_RESULT; + result->act_row = NULL; + strcpy(result->numrowstring,""); + + result->fields = (INGRES_FIELD *)calloc(1, sizeof(INGRES_FIELD) * result->num_fields); + if (result->fields == (INGRES_FIELD *)0) + { printf("Failure allocating %d INGRES_FIELD elements\n",result->num_fields); } + + DEBB(2) + printf("INGgetINGresult, before loop over %d fields\n", result->num_fields); + DEBE + + int i; + for (i=0;inum_fields;++i) + { + memset(result->fields[i].name,'\0',34); + strncpy(result->fields[i].name, + sqlda->sqlvar[i].sqlname.sqlnamec, + sqlda->sqlvar[i].sqlname.sqlnamel); + result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]); + result->fields[i].type = abs(sqlda->sqlvar[i].sqltype); + result->fields[i].flags = (abs(sqlda->sqlvar[i].sqltype)<0) ? 1 : 0; + } + + return result; +} + +void INGfreeINGresult(INGresult *ing_res) +{ + /* TODO: free all rows and fields, then res, not descriptor! */ + if( ing_res != NULL ) + { + /* use of rows is a nasty workaround til I find the reason, + why aggregates like max() don't work + */ + int rows = ing_res->num_rows; + ING_ROW *rowtemp; + ing_res->act_row = ing_res->first_row; + while (ing_res->act_row != NULL && rows > 0) + { + rowtemp = ing_res->act_row->next; + INGfreeRowSpace(ing_res->act_row, ing_res->sqlda); + ing_res->act_row = rowtemp; + --rows; + } + free(ing_res->fields); + } + free(ing_res); + ing_res = NULL; +} + +ING_ROW *INGgetRowSpace(INGresult *ing_res) +{ + IISQLDA *sqlda = ing_res->sqlda; + ING_ROW *row = NULL; + IISQLVAR *vars = NULL; + row = (ING_ROW *)calloc(1,sizeof(ING_ROW)); + if (row == (ING_ROW *)0) + { printf("Failure allocating ING_ROW\n"); } + + vars = (IISQLVAR *)calloc(1,sizeof(IISQLVAR) * sqlda->sqld); + if (vars == (IISQLVAR *)0) + { printf("Failure allocating %d SQLVAR elements\n",sqlda->sqld); } + + row->sqlvar = vars; + row->next = NULL; + + int i; + unsigned short len; /* used for VARCHAR type length */ + for (i=0;isqld;++i) + { + /* make strings out of the data, then the space and assign + (why string? at least it seems that way, looking into the sources) + */ + switch (abs(ing_res->fields[i].type)) + { + case IISQ_VCH_TYPE: + len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len; + DEBB(2) + printf("length of varchar: %d\n", len); + DEBE + vars[i].sqldata = (char *)malloc(len+1); + if (vars[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",len+1); } + memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len); + vars[i].sqldata[len] = '\0'; + break; + case IISQ_CHA_TYPE: + vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1); + if (vars[i].sqldata == (char *)0) + { printf("Failure allocating %d bytes for SQLVAR data\n",ing_res->fields[i].max_length); } + memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen); + vars[i].sqldata[ing_res->fields[i].max_length] = '\0'; + break; + case IISQ_INT_TYPE: + vars[i].sqldata = (char *)malloc(20); + memset(vars[i].sqldata,'\0',20); + sprintf(vars[i].sqldata,"%d",*(int*)sqlda->sqlvar[i].sqldata); + break; + } + vars[i].sqlind = (short *)malloc(sizeof(short)); + if (sqlda->sqlvar[i].sqlind == (short *)0) + { printf("Failure allocating sqlind\n"); } + memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short)); + DEBB(2) + printf("INGgetRowSpace, Field %d, type %d, length %d, name %s\n", + i, sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqllen, ing_res->fields[i].name); + DEBE + } + + return row; +} + + +void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda) +{ + int i; + if (row == NULL || sqlda == NULL) + { + printf("INGfreeRowSpace: one argument is NULL!\n"); + return; + } + + for ( i = 0 ; i < sqlda->sqld ; ++i ) + { + free(row->sqlvar[i].sqldata); + free(row->sqlvar[i].sqlind); + } + free(row->sqlvar); + free(row); +} + +int INGfetchAll(const char *stmt, INGresult *ing_res) +{ + int linecount = 0; + ING_ROW *row; + IISQLDA *desc; + + EXEC SQL BEGIN DECLARE SECTION; + char stmt_buffer[2000]; + EXEC SQL END DECLARE SECTION; + + strcpy(stmt_buffer,stmt); + desc = ing_res->sqlda; + + EXEC SQL DECLARE c2 CURSOR FOR s2; + INGcheck(); + + EXEC SQL OPEN c2; + INGcheck(); + + /* for (linecount=0;sqlca.sqlcode==0;++linecount) */ + while(sqlca.sqlcode==0) + { + EXEC SQL FETCH c2 USING DESCRIPTOR :desc; + INGcheck(); + + if (sqlca.sqlcode == 0) + { + row = INGgetRowSpace(ing_res); /* alloc space for fetched row */ + + /* initialize list when encountered first time */ + if (ing_res->first_row == 0) + { + ing_res->first_row = row; /* head of the list */ + ing_res->first_row->next = NULL; + ing_res->act_row = ing_res->first_row; + } + ing_res->act_row->next = row; /* append row to old act_row */ + ing_res->act_row = row; /* set row as act_row */ + row->row_number = linecount; + ++linecount; + + DEBB(2) + int i; + printf("Row %d ", linecount); + for (i=0;inum_fields;++i) + { printf("F%d:%s ",i,row->sqlvar[i].sqldata); } + printf("\n"); + DEBE + + } + } + + EXEC SQL CLOSE c2; + + ing_res->status = ING_COMMAND_OK; + ing_res->num_rows = linecount; + return linecount; +} + +ING_STATUS INGresultStatus(INGresult *res) +{ + if (res == NULL) {return ING_NO_RESULT;} + return res->status; +} + +void INGrowSeek(INGresult *res, int row_number) +{ + if (res->act_row->row_number == row_number) { return; } + + /* TODO: real error handling */ + if (row_number<0 || row_number>res->num_rows) { return;} + + ING_ROW *trow = res->first_row; + while ( trow->row_number != row_number ) + { trow = trow->next; } + res->act_row = trow; + /* note - can be null - if row_number not found, right? */ +} + +char *INGgetvalue(INGresult *res, int row_number, int column_number) +{ + if (row_number != res->act_row->row_number) + { INGrowSeek(res, row_number); } + return res->act_row->sqlvar[column_number].sqldata; +} + +int INGgetisnull(INGresult *res, int row_number, int column_number) +{ + if (row_number != res->act_row->row_number) + { INGrowSeek(res, row_number); } + return (short)*res->act_row->sqlvar[column_number].sqlind; +} + +int INGntuples(const INGresult *res) +{ + return res->num_rows; +} + +int INGnfields(const INGresult *res) +{ + return res->num_fields; +} + +char *INGfname(const INGresult *res, int column_number) +{ + if ( (column_number > res->num_fields) || (column_number < 0) ) + { return NULL; } + else + { return res->fields[column_number].name; } +} + +short INGftype(const INGresult *res, int column_number) +{ + return res->fields[column_number].type; +} + +INGresult *INGexec(INGconn *conn, const char *query) +{ + /* TODO: error handling -> res->status? */ + IISQLDA *desc = NULL; + INGresult *res = NULL; + int cols = -1; + + EXEC SQL BEGIN DECLARE SECTION; + char stmt[2000]; + EXEC SQL END DECLARE SECTION; + strncpy(stmt,query,strlen(query)); + stmt[strlen(query)]='\0'; + + DEBB(1) + printf("INGexec: query is >>%s<<\n",stmt); + DEBE + + if ((cols = INGgetCols(query)) == 0) + { + DEBB(1) + printf("INGexec: non-select\n"); + DEBE + /* non-select statement - TODO: EXECUTE IMMEDIATE */ + EXEC SQL EXECUTE IMMEDIATE :stmt; + } + else + { + DEBB(1) + printf("INGexec: select\n"); + DEBE + /* select statement */ + desc = INGgetDescriptor(cols, query); + res = INGgetINGresult(desc); + INGfetchAll(query, res); + } + return res; +} + +void INGclear(INGresult *res) +{ + if (res == NULL) { return; } + IISQLDA *desc = res->sqlda; + INGfreeINGresult(res); + INGfreeDescriptor(desc); +} + +INGconn *INGconnectDB(char *dbname, char *user, char *passwd) +{ + if (dbname == NULL || strlen(dbname) == 0) + { return NULL; } + + INGconn *dbconn = (INGconn *)calloc(1,sizeof(INGconn)); + if (dbconn == (INGconn *)0) + { printf("Failure allocating INGconn\n"); } + + EXEC SQL BEGIN DECLARE SECTION; + char ingdbname[24]; + char ingdbuser[32]; + char ingdbpasw[32]; + char conn_name[32]; + int sess_id; + EXEC SQL END DECLARE SECTION; + + strcpy(ingdbname,dbname); + + if ( user != NULL) + { + DEBB(1) + printf("Connection: with user/passwd\n"); + DEBE + strcpy(ingdbuser,user); + if ( passwd != NULL) + { strcpy(ingdbpasw,passwd); } + else + { strcpy(ingdbpasw, ""); } + EXEC SQL CONNECT + :ingdbname + identified by :ingdbuser + dbms_password = :ingdbpasw; + } + else + { + DEBB(1) + printf("Connection: w/ user/passwd\n"); + DEBE + EXEC SQL CONNECT :ingdbname; + } + + EXEC SQL INQUIRE_SQL(:conn_name = connection_name); + EXEC SQL INQUIRE_SQL(:sess_id = session); + + strcpy(dbconn->dbname,ingdbname); + strcpy(dbconn->user,ingdbuser); + strcpy(dbconn->password,ingdbpasw); + strcpy(dbconn->connection_name,conn_name); + dbconn->session_id = sess_id; + + DEBB(1) + printf("Connected to '%s' with user/passwd %s/%s, sessID/name %i/%s\n", + dbconn->dbname, + dbconn->user, + dbconn->password, + dbconn->session_id, + dbconn->connection_name + ); + DEBE + + return dbconn; +} + +void INGdisconnectDB(INGconn *dbconn) +{ + /* TODO: use of dbconn */ + EXEC SQL DISCONNECT; + free(dbconn); +} + +char *INGerrorMessage(const INGconn *conn) +{ + return NULL; +} + +char *INGcmdTuples(INGresult *res) +{ + return res->numrowstring; +} + + +/* TODO? +char *INGerrorMessage(const INGconn *conn); +int INGputCopyEnd(INGconn *conn, const char *errormsg); +int INGputCopyData(INGconn *conn, const char *buffer, int nbytes); +*/ diff --git a/bacula/src/cats/myingres.sh b/bacula/src/cats/myingres.sh new file mode 100644 index 0000000000..2b81c6e457 --- /dev/null +++ b/bacula/src/cats/myingres.sh @@ -0,0 +1,83 @@ +#ifndef _MYINGRES_SH +#define _MYINGRES_SH + +EXEC SQL INCLUDE SQLDA; + +/* ---typedefs--- */ + +typedef struct ing_field { + char name[34]; + int max_length; + unsigned int type; + unsigned int flags; // 1 == not null +} INGRES_FIELD; + +typedef struct ing_row { + IISQLVAR *sqlvar; /* ptr to sqlvar[sqld] for one row */ + struct ing_row *next; + int row_number; +} ING_ROW; + +typedef enum ing_status { + ING_COMMAND_OK, + ING_TUPLES_OK, + ING_NO_RESULT, + ING_NO_ROWS_PROCESSED, + ING_EMPTY_RESULT, + ING_ERROR +} ING_STATUS; + +typedef struct ing_varchar { + short len; + char* value; +} ING_VARCHAR; + +/* It seems, Bacula needs the complete query result stored in one data structure */ +typedef struct ing_result { + IISQLDA *sqlda; /* descriptor */ + INGRES_FIELD *fields; + int num_rows; + int num_fields; + ING_STATUS status; + ING_ROW *first_row; + ING_ROW *act_row; /* just for iterating */ + char numrowstring[10]; + +} INGresult; + +typedef struct ing_conn { + char dbname[24]; + char user[32]; + char password[32]; + char connection_name[32]; + int session_id; +} INGconn; + + +/* ---Prototypes--- */ +int INGcheck(); +ING_STATUS INGresultStatus(INGresult *res); +short INGgetCols(const char *stmt); +IISQLDA *INGgetDescriptor(short numCols, const char *stmt); +void INGfreeDescriptor(IISQLDA *sqlda); +int INGgetTypeSize(IISQLVAR *ingvar); +INGresult *INGgetINGresult(IISQLDA *sqlda); +void INGfreeINGresult(INGresult *ing_res); +ING_ROW *INGgetRowSpace(INGresult *ing_res); +void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda); +int INGfetchAll(const char *stmt, INGresult *ing_res); +void INGrowSeek(INGresult *res, int row_number); +char *INGgetvalue(INGresult *res, int row_number, int column_number); +int INGgetisnull(INGresult *res, int row_number, int column_number); +int INGntuples(const INGresult *res); +int INGnfields(const INGresult *res); +char *INGfname(const INGresult *res, int column_number); +short INGftype(const INGresult *res, int column_number); +INGresult *INGexec(INGconn *db, const char *query); +void INGclear(INGresult *res); +INGconn *INGconnectDB(char *dbname, char *user, char *passwd); +void INGdisconnectDB(INGconn *dbconn); +char *INGerrorMessage(const INGconn *conn); +char *INGcmdTuples(INGresult *res); + +#endif /* _MYINGRES_SH */ diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index 8c875cfc4f..4327955ce7 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -35,7 +35,7 @@ * * Kern Sibbald, March 2000 * - * Version $Id$ + * Version $Id: sql.c 8034 2008-11-11 14:33:46Z ricozz $ */ /* The following is necessary so that we do not include @@ -46,7 +46,7 @@ #include "bacula.h" #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI uint32_t bacula_db_version = 0; @@ -84,6 +84,8 @@ B_DB *db_init(JCR *jcr, const char *db_driver, const char *db_name, const char * db_type = SQL_TYPE_MYSQL; #elif HAVE_POSTGRESQL db_type = SQL_TYPE_POSTGRESQL; +#elif HAVE_INGRES + db_type = SQL_TYPE_INGRES; #elif HAVE_SQLITE db_type = SQL_TYPE_SQLITE; #elif HAVE_SQLITE3 @@ -454,6 +456,23 @@ void db_start_transaction(JCR *jcr, B_DB *mdb) db_unlock(mdb); #endif +#ifdef HAVE_INGRES + if (!mdb->allow_transactions) { + return; + } + db_lock(mdb); + /* Allow only 25,000 changes per transaction */ + if (mdb->transaction && mdb->changes > 25000) { + db_end_transaction(jcr, mdb); + } + if (!mdb->transaction) { + db_sql_query(mdb, "BEGIN", NULL, NULL); /* begin transaction */ + Dmsg0(400, "Start Ingres transaction\n"); + mdb->transaction = 1; + } + db_unlock(mdb); +#endif + #ifdef HAVE_DBI if (db_type == SQL_TYPE_SQLITE) { if (!mdb->allow_transactions) { @@ -522,6 +541,23 @@ void db_end_transaction(JCR *jcr, B_DB *mdb) db_unlock(mdb); #endif + + +#ifdef HAVE_INGRES + if (!mdb->allow_transactions) { + return; + } + db_lock(mdb); + if (mdb->transaction) { + db_sql_query(mdb, "COMMIT", NULL, NULL); /* end transaction */ + mdb->transaction = 0; + Dmsg1(400, "End Ingres transaction changes=%d\n", mdb->changes); + } + mdb->changes = 0; + db_unlock(mdb); +#endif + + #ifdef HAVE_POSTGRESQL if (!mdb->allow_transactions) { return; @@ -842,4 +878,4 @@ void db_debug_print(JCR *jcr, FILE *fp) } } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES*/ diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index e597d46609..736db4420f 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -30,7 +30,7 @@ * * Kern Sibbald, March 2000 * - * Version $Id$ + * Version $Id: sql_create.c 8407 2009-01-28 10:47:21Z ricozz $ */ /* The following is necessary so that we do not include @@ -43,7 +43,7 @@ static const int dbglevel = 100; -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * @@ -1255,4 +1255,4 @@ bail_out: return ret; } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */ diff --git a/bacula/src/cats/sql_delete.c b/bacula/src/cats/sql_delete.c index 4d78bb91f1..134b9e1963 100644 --- a/bacula/src/cats/sql_delete.c +++ b/bacula/src/cats/sql_delete.c @@ -3,7 +3,7 @@ * * Kern Sibbald, December 2000 * - * Version $Id$ + * Version $Id: sql_delete.c 7380 2008-07-14 10:42:59Z kerns $ */ /* Bacula® - The Network Backup Solution @@ -44,7 +44,7 @@ #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * * Generic Routines (or almost generic) @@ -237,4 +237,4 @@ int db_purge_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES */ diff --git a/bacula/src/cats/sql_find.c b/bacula/src/cats/sql_find.c index 1ce51e3ff1..827f0cb00d 100644 --- a/bacula/src/cats/sql_find.c +++ b/bacula/src/cats/sql_find.c @@ -34,7 +34,7 @@ * * Kern Sibbald, December 2000 * - * Version $Id$ + * Version $Id: sql_find.c 8508 2009-03-07 20:59:46Z kerns $ */ @@ -46,7 +46,7 @@ #include "bacula.h" #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 3147b3a73c..95dbf32fb3 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -33,7 +33,6 @@ * * Kern Sibbald, March 2000 * - * Version $Id: sql_get.c 8918 2009-06-23 11:56:35Z ricozz $ */ @@ -45,7 +44,7 @@ #include "bacula.h" #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * @@ -1267,4 +1266,4 @@ bail_out: return ret; } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */ diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index d5a59b69f7..6c005ca517 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -30,7 +30,7 @@ * * Kern Sibbald, March 2000 * - * Version $Id$ + * Version $Id: sql_list.c 8508 2009-03-07 20:59:46Z kerns $ */ @@ -44,7 +44,7 @@ extern int db_type; -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * @@ -509,4 +509,4 @@ db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER * } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES */ diff --git a/bacula/src/cats/sql_update.c b/bacula/src/cats/sql_update.c index 3f091f456f..7dba22031e 100644 --- a/bacula/src/cats/sql_update.c +++ b/bacula/src/cats/sql_update.c @@ -30,7 +30,7 @@ * * Kern Sibbald, March 2000 * - * Version $Id$ + * Version $Id: sql_update.c 8478 2009-02-18 20:11:55Z kerns $ */ /* The following is necessary so that we do not include @@ -41,7 +41,7 @@ #include "bacula.h" #include "cats.h" -#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI +#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI /* ----------------------------------------------------------------------- * @@ -482,4 +482,4 @@ db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) } } -#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/ +#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES */ diff --git a/bacula/src/cats/update_bacula_tables.in b/bacula/src/cats/update_bacula_tables.in index 8ce59a573b..cd3f3b8e9f 100755 --- a/bacula/src/cats/update_bacula_tables.in +++ b/bacula/src/cats/update_bacula_tables.in @@ -1,7 +1,7 @@ #!/bin/sh # # This routine alters the appropriately configured -# Bacula tables for PostgreSQL, MySQL, or SQLite. +# Bacula tables for PostgreSQL, Ingres, MySQL, or SQLite. # if test xsqlite3 = x@DB_TYPE@ ; then echo "Altering SQLite tables" @@ -11,6 +11,10 @@ if test xmysql = x@DB_TYPE@ ; then echo "Altering MySQL tables" @scriptdir@/update_mysql_tables $* fi +if test xingres = x@DB_TYPE@ ; then + echo "Altering Ingres tables" + @scriptdir@/update_ingres_tables $* +fi if test xpostgresql = x@DB_TYPE@ ; then echo "Altering PostgreSQL tables" @scriptdir@/update_postgresql_tables $* diff --git a/bacula/src/cats/update_ingres_tables.in b/bacula/src/cats/update_ingres_tables.in new file mode 100755 index 0000000000..9b9099c190 --- /dev/null +++ b/bacula/src/cats/update_ingres_tables.in @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Shell script to update Ingres tables (without any function for now) +# +echo " " +echo "This script will update a Bacula Ingres database (if needed)" +echo " " +bindir=@SQL_BINDIR@ +db_name=@db_name@ + +echo "Update of Bacula Ingres tables succeeded. (nothing to do)" +exit 0 -- 2.39.5