]> git.sur5r.net Git - bacula/bacula/commitdiff
Add commit from Stefan Reddig
authorStefan Reddig <Stefan.Reddig@ingres.com>
Fri, 6 Nov 2009 20:42:05 +0000 (21:42 +0100)
committerKern Sibbald <kern@sibbald.com>
Mon, 4 Jan 2010 11:28:19 +0000 (12:28 +0100)
27 files changed:
bacula/src/cats/Makefile.in
bacula/src/cats/cats.h
bacula/src/cats/create_bacula_database.in
bacula/src/cats/create_ingres_database.in [new file with mode: 0755]
bacula/src/cats/drop_bacula_database.in
bacula/src/cats/drop_bacula_tables.in
bacula/src/cats/drop_ingres_database.in [new file with mode: 0755]
bacula/src/cats/drop_ingres_tables.in [new file with mode: 0755]
bacula/src/cats/grant_bacula_privileges.in
bacula/src/cats/grant_ingres_privileges.in [new file with mode: 0755]
bacula/src/cats/ingres.c [new file with mode: 0755]
bacula/src/cats/ingres.in [new file with mode: 0755]
bacula/src/cats/make_bacula_tables.in
bacula/src/cats/make_ingres_tables.in [new file with mode: 0755]
bacula/src/cats/myingres.c [new file with mode: 0644]
bacula/src/cats/myingres.h [new file with mode: 0644]
bacula/src/cats/myingres.sc [new file with mode: 0644]
bacula/src/cats/myingres.sh [new file with mode: 0644]
bacula/src/cats/sql.c
bacula/src/cats/sql_create.c
bacula/src/cats/sql_delete.c
bacula/src/cats/sql_find.c
bacula/src/cats/sql_get.c
bacula/src/cats/sql_list.c
bacula/src/cats/sql_update.c
bacula/src/cats/update_bacula_tables.in
bacula/src/cats/update_ingres_tables.in [new file with mode: 0755]

index e39266f6878702ef1d2ec2070e656545c3713803..7afabe084eaee6da19e93860f776a7f084b3c526 100644 (file)
@@ -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
index 8c0d4f90c45129e786595a2c2f06fd2254f98a79..94b6532abeab5724da2b54f7a0890c0c4fb62292 100644 (file)
@@ -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
 
index 4db10bfe7dd265f0d91c8514a7149add467a2429..16008698ba5353a96f8d7ecc7bbadbd77f0dc0cc 100644 (file)
@@ -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 (executable)
index 0000000..a287352
--- /dev/null
@@ -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
index bd1dbf158c177a2a6e30656965e35151460e6836..d542af0c3a0ff6f82ad5dc8b7648311374991bcc 100755 (executable)
@@ -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
index 97509289ca3095b3b912d4bfe395e89b75bc6b98..459d504661f2ad3ebb3e9c60182ede863fccf7ae 100755 (executable)
@@ -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 (executable)
index 0000000..23c75fe
--- /dev/null
@@ -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 (executable)
index 0000000..ad7f23c
--- /dev/null
@@ -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} $* <<END-OF-DATA
+drop table unsavedfiles;
+drop table basefiles;
+drop table jobmedia;
+drop table file;
+drop table job;
+drop table jobhisto;
+drop table media;
+drop table client;
+drop table pool;
+drop table fileset;
+drop table path;
+drop table filename;
+drop table counters;
+drop table version;
+drop table CDImages;
+drop table Device;
+drop table Storage;
+drop table MediaType;
+drop table Status;
+drop table MAC;
+drop table log;
+drop table Location;
+drop table locationlog;
+END-OF-DATA
+pstat=$?
+if test $pstat = 0; 
+then
+   echo "Deletion of Bacula Ingres tables succeeded."
+else
+   echo "Deletion of Bacula Ingres tables failed."
+fi
+exit $pstat
index eb290b7eb1be37787579b2d266b333fafaef5af7..d18a8731fb8b72a2880b8e33eeb45ba987b943fb 100755 (executable)
@@ -11,6 +11,9 @@ else
   if test xpostgresql = x@DB_TYPE@ ; then 
     echo "Granting PostgreSQL privileges"
     @scriptdir@/grant_postgresql_privileges $*
+  elif test xingres = x@DB_TYPE@ ; then
+    echo "Granting Ingres privileges"
+    @scriptdir@/grant_ingres_privileges $*
   else
     if test xsqlite3 = x@DB_TYPE@ ; then 
       echo "Granting SQLite privileges"
diff --git a/bacula/src/cats/grant_ingres_privileges.in b/bacula/src/cats/grant_ingres_privileges.in
new file mode 100755 (executable)
index 0000000..4e447da
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# shell script to grant privileges to the bacula database
+#
+db_user=${db_user:-@db_user@}
+bindir=@SQL_BINDIR@
+db_name=${db_name:-@db_name@}
+
+if sql ${db_name} $* <<END-OF-DATA
+
+create user ${db_user};
+
+-- for tables
+grant all on unsavedfiles to ${db_user};
+grant all on basefiles   to ${db_user};
+grant all on jobmedia    to ${db_user};
+grant all on file        to ${db_user};
+grant all on job         to ${db_user};
+grant all on media       to ${db_user};
+grant all on client      to ${db_user};
+grant all on pool        to ${db_user};
+grant all on fileset     to ${db_user};
+grant all on path        to ${db_user};
+grant all on filename    to ${db_user};
+grant all on counters    to ${db_user};
+grant all on version     to ${db_user};
+grant all on cdimages    to ${db_user};
+grant all on mediatype   to ${db_user};
+grant all on storage     to ${db_user};
+grant all on device      to ${db_user};
+grant all on status      to ${db_user};
+grant all on location    to ${db_user};
+grant all on locationlog  to ${db_user};
+grant all on log         to ${db_user};
+grant all on jobhisto    to ${db_user};
+
+-- for sequences on those tables
+
+grant select, update on filename_filenameid_seq    to ${db_user};
+grant select, update on path_pathid_seq           to ${db_user};
+grant select, update on fileset_filesetid_seq     to ${db_user};
+grant select, update on pool_poolid_seq           to ${db_user};
+grant select, update on client_clientid_seq       to ${db_user};
+grant select, update on media_mediaid_seq         to ${db_user};
+grant select, update on job_jobid_seq             to ${db_user};
+grant select, update on file_fileid_seq           to ${db_user};
+grant select, update on jobmedia_jobmediaid_seq    to ${db_user};
+grant select, update on basefiles_baseid_seq      to ${db_user};
+grant select, update on storage_storageid_seq     to ${db_user};
+grant select, update on mediatype_mediatypeid_seq  to ${db_user};
+grant select, update on device_deviceid_seq       to ${db_user};
+grant select, update on location_locationid_seq    to ${db_user};
+grant select, update on locationlog_loclogid_seq   to ${db_user};
+grant select, update on log_logid_seq             to ${db_user};
+
+END-OF-DATA
+then
+   echo "Privileges for ${db_user} granted on ${db_name}."
+   exit 0
+else
+   echo "Error creating privileges."
+   exit 1
+fi
diff --git a/bacula/src/cats/ingres.c b/bacula/src/cats/ingres.c
new file mode 100755 (executable)
index 0000000..5a92918
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ * Bacula Catalog Database routines specific to Ingres
+ *   These are Ingres specific routines
+ *
+ *    Stefan Reddig, June 2009
+ *    based uopn work done 
+ *    by Dan Langille, December 2003 and
+ *    by Kern Sibbald, March 2000
+ *
+ */
+
+
+/* The following is necessary so that we do not include
+ * the dummy external definition of DB.
+ */
+#define __SQL_C                       /* indicate that this is sql.c */
+
+#include "bacula.h"
+#include "cats.h"
+
+#ifdef HAVE_INGRES
+
+#include "myingres.h"
+
+/* -----------------------------------------------------------------------
+ *
+ *   Ingres dependent defines and subroutines
+ *
+ * -----------------------------------------------------------------------
+ */
+
+/* List of open databases */  /* SRE: needed for ingres? */
+static BQUEUE db_list = {&db_list, &db_list};
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Retrieve database type
+ */
+const char *
+db_get_type(void)
+{
+   return "Ingres";
+}
+
+/*
+ * Initialize database data structure. In principal this should
+ * never have errors, or it is really fatal.
+ */
+B_DB *
+db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password,
+                 const char *db_address, int db_port, const char *db_socket,
+                 int mult_db_connections)
+{
+   B_DB *mdb;
+
+   if (!db_user) {
+      Jmsg(jcr, M_FATAL, 0, _("A user name for Ingres must be supplied.\n"));
+      return NULL;
+   }
+   P(mutex);                          /* lock DB queue */
+   if (!mult_db_connections) {
+      /* Look to see if DB already open */
+      for (mdb=NULL; (mdb=(B_DB *)qnext(&db_list, &mdb->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 (executable)
index 0000000..a3adde6
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# shell script to create Bacula PostgreSQL tables
+#
+bindir=@SQL_BINDIR@
+db_name=@db_name@
+
+sql $* ${db_name}
index c8469f12bf04e11fb7f36f201074d4c2e36e47c9..6d9221d1e9e4f5c59428ee4bb5a4292721835687 100755 (executable)
@@ -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 (executable)
index 0000000..04a6ec2
--- /dev/null
@@ -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} <<END-OF-DATA
+
+SET AUTOCOMMIT ON\g
+
+CREATE SEQUENCE filename_seq;
+CREATE TABLE filename
+(
+    filenameid       integer     not null default filename_seq.nextval,
+    name             text        not null,
+    primary key (filenameid)
+);
+
+ALTER TABLE filename ALTER COLUMN name SET STATISTICS 1000;
+CREATE UNIQUE INDEX filename_name_idx on filename (name);
+
+CREATE SEQUENCE path_seq;
+CREATE TABLE path
+(
+    pathid           integer     not null default path_seq.nextval,
+    path             text        not null,
+    primary key (pathid)
+);
+
+ALTER TABLE path ALTER COLUMN path SET STATISTICS 1000;
+CREATE UNIQUE INDEX path_name_idx on path (path);
+
+CREATE SEQUENCE file_seq;
+CREATE TABLE file
+(
+    fileid           integer8    not null default file_seq.nextval,
+    fileindex        integer     not null  default 0,
+    jobid            integer     not null,
+    pathid           integer     not null,
+    filenameid       integer     not null,
+    markid           integer     not null  default 0,
+    lstat            text        not null,
+    md5              text        not null,
+    primary key (fileid)
+);
+
+CREATE INDEX file_jobid_idx on file (jobid);
+CREATE INDEX file_fp_idx on file (filenameid, pathid);
+
+--
+-- Add this if you have a good number of job
+-- that run at the same time
+-- ALTER SEQUENCE file_fileid_seq CACHE 1000;
+
+--
+-- Possibly add one or more of the following indexes
+--  if your Verifies are too slow.
+--
+-- CREATE INDEX file_pathid_idx on file(pathid);
+-- CREATE INDEX file_filenameid_idx on file(filenameid);
+-- CREATE INDEX file_jpfid_idx on file (jobid, pathid, filenameid);
+
+CREATE SEQUENCE job_seq;
+CREATE TABLE job
+(
+    jobid            integer     not null default job_seq.nextval,
+    job              text        not null,
+    name             text        not null,
+    type             char(1)     not null,
+    level            char(1)     not null,
+    clientid         integer     default 0,
+    jobstatus        char(1)     not null,
+    schedtime        timestamp   without time zone,
+    starttime        timestamp   without time zone,
+    endtime          timestamp   without time zone,
+    realendtime       timestamp   without time zone,
+    jobtdate         bigint      default 0,
+    volsessionid      integer    default 0,
+    volsessiontime    integer    default 0,
+    jobfiles         integer     default 0,
+    jobbytes         bigint      default 0,
+    readbytes        bigint      default 0,
+    joberrors        integer     default 0,
+    jobmissingfiles   integer    default 0,
+    poolid           integer     default 0,
+    filesetid        integer     default 0,
+    purgedfiles       smallint   default 0,
+    hasbase          smallint    default 0,
+    priorjobid       integer     default 0,
+    primary key (jobid)
+);
+
+CREATE INDEX job_name_idx on job (name);
+
+-- Create a table like Job for long term statistics 
+CREATE TABLE JobHisto (LIKE Job);
+CREATE INDEX jobhisto_idx ON jobhisto ( starttime );
+
+
+CREATE SEQUENCE Location_seq;
+CREATE TABLE Location (
+   LocationId        integer     not null default Location_seq.nextval,
+   Location          text        not null,
+   Cost              integer     default 0,
+   Enabled           smallint,
+   primary key (LocationId)
+);
+
+
+CREATE SEQUENCE fileset_seq;
+CREATE TABLE fileset
+(
+    filesetid        integer     not null default fileset_seq.nextval,
+    fileset          text        not null,
+    md5              text        not null,
+    createtime       timestamp without time zone not null,
+    primary key (filesetid)
+);
+
+CREATE INDEX fileset_name_idx on fileset (fileset);
+
+CREATE SEQUENCE jobmedia_seq;
+CREATE TABLE jobmedia
+(
+    jobmediaid       integer     not null default jobmedia_seq.nestval,
+    jobid            integer     not null,
+    mediaid          integer     not null,
+    firstindex       integer     default 0,
+    lastindex        integer     default 0,
+    startfile        integer     default 0,
+    endfile          integer     default 0,
+    startblock       bigint      default 0,
+    endblock         bigint      default 0,
+    volindex         integer     default 0,
+    copy             integer     default 0,
+    primary key (jobmediaid)
+);
+
+CREATE INDEX job_media_job_id_media_id_idx on jobmedia (jobid, mediaid);
+
+CREATE SEQUENCE media_seq;
+CREATE TABLE media
+(
+    mediaid          integer     not null default media_seq.nextval,
+    volumename       text        not null,
+    slot             integer     default 0,
+    poolid           integer     default 0,
+    mediatype        text        not null,
+    mediatypeid       integer    default 0,
+    labeltype        integer     default 0,
+    firstwritten      timestamp   without time zone,
+    lastwritten       timestamp   without time zone,
+    labeldate        timestamp   without time zone,
+    voljobs          integer     default 0,
+    volfiles         integer     default 0,
+    volblocks        integer     default 0,
+    volmounts        integer     default 0,
+    volbytes         bigint      default 0,
+    volparts         integer     default 0,
+    volerrors        integer     default 0,
+    volwrites        integer     default 0,
+    volcapacitybytes  bigint     default 0,
+    volstatus        text        not null
+       check (volstatus in ('Full','Archive','Append',
+             'Recycle','Purged','Read-Only','Disabled',
+             'Error','Busy','Used','Cleaning','Scratch')),
+    enabled          smallint    default 1,
+    recycle          smallint    default 0,
+    ActionOnPurge     smallint    default 0,
+    volretention      bigint     default 0,
+    voluseduration    bigint     default 0,
+    maxvoljobs       integer     default 0,
+    maxvolfiles       integer    default 0,
+    maxvolbytes       bigint     default 0,
+    inchanger        smallint    default 0,
+    StorageId        integer     default 0,
+    DeviceId         integer     default 0,
+    mediaaddressing   smallint   default 0,
+    volreadtime       bigint     default 0,
+    volwritetime      bigint     default 0,
+    endfile          integer     default 0,
+    endblock         bigint      default 0,
+    LocationId       integer     default 0,
+    recyclecount      integer    default 0,
+    initialwrite      timestamp   without time zone,
+    scratchpoolid     integer    default 0,
+    recyclepoolid     integer    default 0,
+    comment          text,
+    primary key (mediaid)
+);
+
+create unique index media_volumename_id on media (volumename);
+
+CREATE SEQUENCE MediaType_seq;
+CREATE TABLE MediaType (
+   MediaTypeId INTEGER DEFAULT MediaType_seq.NEXTVAL,
+   MediaType TEXT NOT NULL,
+   ReadOnly INTEGER DEFAULT 0,
+   PRIMARY KEY(MediaTypeId)
+   );
+
+CREATE SEQUENCE Storage_seq;
+CREATE TABLE Storage (
+   StorageId INTEGER DEFAULT Storage_seq.NEXTVAL,
+   Name TEXT NOT NULL,
+   AutoChanger INTEGER DEFAULT 0,
+   PRIMARY KEY(StorageId)
+   );
+
+CREATE SEQUENCE Device_seq;
+CREATE TABLE Device (
+   DeviceId INTEGER DEFAULT Device_seq.NEXTVAL,
+   Name TEXT NOT NULL,
+   MediaTypeId INTEGER NOT NULL,
+   StorageId INTEGER NOT NULL,
+   DevMounts INTEGER NOT NULL DEFAULT 0,
+   DevReadBytes BIGINT NOT NULL DEFAULT 0,
+   DevWriteBytes BIGINT NOT NULL DEFAULT 0,
+   DevReadBytesSinceCleaning BIGINT NOT NULL DEFAULT 0,
+   DevWriteBytesSinceCleaning BIGINT NOT NULL DEFAULT 0,
+   DevReadTime BIGINT NOT NULL DEFAULT 0,
+   DevWriteTime BIGINT NOT NULL DEFAULT 0,
+   DevReadTimeSinceCleaning BIGINT NOT NULL DEFAULT 0,
+   DevWriteTimeSinceCleaning BIGINT NOT NULL DEFAULT 0,
+   CleaningDate timestamp without time zone,
+   CleaningPeriod BIGINT NOT NULL DEFAULT 0,
+   PRIMARY KEY(DeviceId)
+   );
+
+
+CREATE SEQUENCE pool_seq;
+CREATE TABLE pool
+(
+    poolid           integer     not null default pool_seq.nextval,
+    name             text        not null,
+    numvols          integer     default 0,
+    maxvols          integer     default 0,
+    useonce          smallint    default 0,
+    usecatalog       smallint    default 0,
+    acceptanyvolume   smallint   default 0,
+    volretention      bigint     default 0,
+    voluseduration    bigint     default 0,
+    maxvoljobs       integer     default 0,
+    maxvolfiles       integer    default 0,
+    maxvolbytes       bigint     default 0,
+    autoprune        smallint    default 0,
+    recycle          smallint    default 0,
+    ActionOnPurge     smallint    default 0,
+    pooltype         text                          
+      check (pooltype in ('Backup','Copy','Cloned','Archive','Migration','Scratch')),
+    labeltype        integer     default 0,
+    labelformat       text       not null,
+    enabled          smallint    default 1,
+    scratchpoolid     integer    default 0,
+    recyclepoolid     integer    default 0,
+    NextPoolId       integer     default 0,
+    MigrationHighBytes BIGINT    DEFAULT 0,
+    MigrationLowBytes  BIGINT    DEFAULT 0,
+    MigrationTime      BIGINT    DEFAULT 0,
+    primary key (poolid)
+);
+
+CREATE INDEX pool_name_idx on pool (name);
+
+CREATE SEQUENCE client_seq;
+CREATE TABLE client
+(
+    clientid         integer     not null default client_seq.nextval,
+    name             text        not null,
+    uname            text        not null,
+    autoprune        smallint    default 0,
+    fileretention     bigint     default 0,
+    jobretention      bigint     default 0,
+    primary key (clientid)
+);
+
+create unique index client_name_idx on client (name);
+
+CREATE SEQUENCE Log_seq;
+CREATE TABLE Log
+(
+    LogId            integer     not null default Log_seq.nextval,
+    JobId            integer     not null,
+    Time             timestamp   without time zone,
+    LogText          text        not null,
+    primary key (LogId)
+);
+create index log_name_idx on Log (JobId);
+
+CREATE SEQUENCE LocationLog_seq;
+CREATE TABLE LocationLog (
+   LocLogId INTEGER NOT NULL DEFAULT LocationLog_seq.NEXTVAL,
+   Date timestamp   without time zone,
+   Comment TEXT NOT NULL,
+   MediaId INTEGER DEFAULT 0,
+   LocationId INTEGER DEFAULT 0,
+   newvolstatus text not null
+       check (newvolstatus in ('Full','Archive','Append',
+             'Recycle','Purged','Read-Only','Disabled',
+             'Error','Busy','Used','Cleaning','Scratch')),
+   newenabled smallint,
+   PRIMARY KEY(LocLogId)
+);
+
+
+
+CREATE TABLE counters
+(
+    counter          text        not null,
+    minvalue         integer     default 0,
+    maxvalue         integer     default 0,
+    currentvalue      integer    default 0,
+    wrapcounter       text       not null,
+    primary key (counter)
+);
+
+
+
+CREATE SEQUENCE basefiles_seq;
+CREATE TABLE basefiles
+(
+    baseid           integer               not null default basefiles_seq.nextval,
+    jobid            integer               not null,
+    fileid           bigint                not null,
+    fileindex        integer                       ,
+    basejobid        integer                       ,
+    primary key (baseid)
+);
+
+CREATE TABLE unsavedfiles
+(
+    UnsavedId        integer               not null,
+    jobid            integer               not null,
+    pathid           integer               not null,
+    filenameid       integer               not null,
+    primary key (UnsavedId)
+);
+
+CREATE TABLE CDImages 
+(
+   MediaId integer not null,
+   LastBurn timestamp without time zone not null,
+   primary key (MediaId)
+);
+
+
+CREATE TABLE version
+(
+    versionid        integer               not null
+);
+
+CREATE TABLE Status (
+   JobStatus CHAR(1) NOT NULL,
+   JobStatusLong TEXT, 
+   PRIMARY KEY (JobStatus)
+   );
+
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('C', 'Created, not yet running');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('R', 'Running');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('B', 'Blocked');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('T', 'Completed successfully');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('E', 'Terminated with errors');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('e', 'Non-fatal error');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('f', 'Fatal error');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('D', 'Verify found differences');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('A', 'Canceled by user');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('F', 'Waiting for Client');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('S', 'Waiting for Storage daemon');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('m', 'Waiting for new media');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('M', 'Waiting for media mount');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('s', 'Waiting for storage resource');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('j', 'Waiting for job resource');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('c', 'Waiting for client resource');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('d', 'Waiting on maximum jobs');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('t', 'Waiting on start time');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('p', 'Waiting on higher priority jobs');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('a', 'SD despooling attributes');
+INSERT INTO Status (JobStatus,JobStatusLong) VALUES
+   ('i', 'Doing batch insert file records');
+
+INSERT INTO Version (VersionId) VALUES (11);
+
+-- Make sure we have appropriate permissions
+\g
+
+END-OF-DATA
+pstat=$?
+if test $pstat = 0; 
+then
+   echo "Creation of Bacula Ingres tables succeeded."
+else
+   echo "Creation of Bacula Ingres tables failed."
+fi
+exit $pstat
diff --git a/bacula/src/cats/myingres.c b/bacula/src/cats/myingres.c
new file mode 100644 (file)
index 0000000..289f5d7
--- /dev/null
@@ -0,0 +1,489 @@
+# include "/opt/Ingres/IngresII/ingres/files/eqdefc.h"
+# include "/opt/Ingres/IngresII/ingres/files/eqsqlca.h"
+    extern IISQLCA sqlca;   /* SQL Communications Area */
+# include "/opt/Ingres/IngresII/ingres/files/eqsqlda.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#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;i<sqlda->sqld;++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;i<result->num_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;i<sqlda->sqld;++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;i<ing_res->num_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 (file)
index 0000000..7a8575e
--- /dev/null
@@ -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 (file)
index 0000000..89a2562
--- /dev/null
@@ -0,0 +1,509 @@
+EXEC SQL INCLUDE SQLCA;
+EXEC SQL INCLUDE SQLDA;
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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;i<sqlda->sqld;++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;i<result->num_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;i<sqlda->sqld;++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;i<ing_res->num_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 (file)
index 0000000..2b81c6e
--- /dev/null
@@ -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 */
index 8c875cfc4f7bc449f0f325f0729b899064788540..4327955ce77bad370b02e210f2259c994e50d3f3 100644 (file)
@@ -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*/
index e597d46609341b3c4a35fa7230552d8b2e49f926..736db4420f1a00568e6dc30f0c8ccd3b0417d29b 100644 (file)
@@ -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 */
index 4d78bb91f1955066a44e8b782d2c5fcf15d6514d..134b9e1963fb9e46643bfc78dc9a3c6677450403 100644 (file)
@@ -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 */
index 1ce51e3ff1191452325c4aea2bf63e0a4919f93d..827f0cb00dc5f49ecceb7cccf481d56075d86b8d 100644 (file)
@@ -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
 
 /* -----------------------------------------------------------------------
  *
index 3147b3a73c2397ec3f4ce8e2906a7785e687c64b..95dbf32fb30a2361a443c019be88b34b81833e4a 100644 (file)
@@ -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 */
index d5a59b69f7740e0dfb3060dfd02699dbb2efe84d..6c005ca517b37f2d3202ec8ae7d364e2cb0a8d83 100644 (file)
@@ -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 */
index 3f091f456f618180cbc2673d2febd024c23400c4..7dba22031e76fb8811f965e045f6acb8df91b818 100644 (file)
@@ -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 */
index 8ce59a573b4f586456e43854c84cca2c53d6df99..cd3f3b8e9f6fc2b42ec87c2ee3a4893a96c83349 100755 (executable)
@@ -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 (executable)
index 0000000..9b9099c
--- /dev/null
@@ -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