]> git.sur5r.net Git - bacula/bacula/commitdiff
Implement db_big_sql_query() that uses cursor on PostgreSQL and limit memory usage...
authorEric Bollengier <eric@eb.homelinux.org>
Fri, 28 Jan 2011 13:41:55 +0000 (14:41 +0100)
committerKern Sibbald <kern@sibbald.com>
Sat, 20 Apr 2013 12:39:56 +0000 (14:39 +0200)
By default on PostgreSQL, db_sql_query() stores the query results in
memory, it can take lots of resources when dealing with job output,
accurate or restore.  This new mode uses cursors and fetches results
100 by 100.

db_list_xxx functions doesn't store the entire query result anymore.
Commands like "list joblog" or "list files" are now stable in memory
usage. (for MySQL and PostgreSQL)

bacula/src/cats/bdb_postgresql.h
bacula/src/cats/cats.h
bacula/src/cats/postgresql.c
bacula/src/cats/protos.h
bacula/src/cats/sql.c
bacula/src/cats/sql_get.c
bacula/src/cats/sql_glue.c
bacula/src/cats/sql_glue.h
bacula/src/cats/sql_list.c

index 10ccda1c065ccc80c2576e2e361d3a448f3e9aa8..ee57926efc320d37f628765f42b025b124c193dd 100644 (file)
@@ -33,6 +33,7 @@ class B_DB_POSTGRESQL: public B_DB_PRIV {
 private:
    PGconn *m_db_handle;
    PGresult *m_result;
+   POOLMEM *m_buf;                        /* Buffer to manipulate queries */
 
 public:
    B_DB_POSTGRESQL(JCR *jcr, const char *db_driver, const char *db_name,
@@ -52,6 +53,7 @@ public:
    void db_start_transaction(JCR *jcr);
    void db_end_transaction(JCR *jcr);
    bool db_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx);
+   bool db_big_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx);
    void sql_free_result(void);
    SQL_ROW sql_fetch_row(void);
    bool sql_query(const char *query, int flags=0);
index a41ea7ac55520762e4be33b8b4126fe941f374c2..d70540f6eabe1d34a6bd536fa04db898bd53d778 100644 (file)
@@ -490,6 +490,12 @@ public:
    virtual void db_start_transaction(JCR *jcr) = 0;
    virtual void db_end_transaction(JCR *jcr) = 0;
    virtual bool db_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx) = 0;
+
+   /* By default, we use db_sql_query */
+   virtual bool db_big_sql_query(const char *query, 
+                                 DB_RESULT_HANDLER *result_handler, void *ctx) {
+      return db_sql_query(query, result_handler, ctx);
+   };
 };
 
 /* sql_query Query Flags */
@@ -505,10 +511,46 @@ public:
 #include "jcr.h"
 #include "sql_cmds.h"
 
+/* Object used in db_list_xxx function */
+class LIST_CTX {
+public:
+   char line[256];              /* Used to print last dash line */
+   int32_t num_rows;
+
+   e_list_type type;            /* Vertical/Horizontal */
+   DB_LIST_HANDLER *send;       /* send data back */
+   bool once;                   /* Used to print header one time */
+   void *ctx;                   /* send() user argument */
+   B_DB *mdb;
+   JCR *jcr;
+
+   void empty() {
+      once = false;
+      line[0] = '\0';
+   }
+
+   void send_dashes() {
+      if (*line) {
+         send(ctx, line);
+      }
+   }
+
+   LIST_CTX(JCR *j, B_DB *m, DB_LIST_HANDLER *h, void *c, e_list_type t) {
+      line[0] = '\0';
+      once = false;
+      num_rows = 0;
+      type = t;
+      send = h;
+      ctx = c;
+      jcr = j;
+      mdb = m;
+   }
+};
+
 /*
  * Some functions exported by sql.c for use within the cats directory.
  */
-void list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type);
+int list_result(void *vctx, int cols, char **row);
 void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx);
 int get_sql_record_max(JCR *jcr, B_DB *mdb);
 bool check_tables_version(JCR *jcr, B_DB *mdb);
index a4b216366730d8a2cfedf43425d56ae334612fc8..1371aa5df9d5923fca19e0425e83278d45e65a09 100644 (file)
@@ -118,6 +118,7 @@ B_DB_POSTGRESQL::B_DB_POSTGRESQL(JCR *jcr,
    path = get_pool_memory(PM_FNAME);
    esc_name = get_pool_memory(PM_FNAME);
    esc_path = get_pool_memory(PM_FNAME);
+   m_buf =  get_pool_memory(PM_FNAME);
    m_allow_transactions = mult_db_connections;
 
    /*
@@ -246,6 +247,7 @@ bool B_DB_POSTGRESQL::db_open_database(JCR *jcr)
    }
 
    sql_query("SET datestyle TO 'ISO, YMD'");
+   sql_query("SET cursor_tuple_fraction=1");
    
    /*
     * Tell PostgreSQL we are using standard conforming strings
@@ -285,6 +287,7 @@ void B_DB_POSTGRESQL::db_close_database(JCR *jcr)
       free_pool_memory(path);
       free_pool_memory(esc_name);
       free_pool_memory(esc_path);
+      free_pool_memory(m_buf);
       if (m_db_driver) {
          free(m_db_driver);
       }
@@ -455,6 +458,72 @@ void B_DB_POSTGRESQL::db_end_transaction(JCR *jcr)
    db_unlock(this);
 }
 
+
+/*
+ * Submit a general SQL command (cmd), and for each row returned,
+ * the result_handler is called with the ctx.
+ */
+bool B_DB_POSTGRESQL::db_big_sql_query(const char *query, 
+                                       DB_RESULT_HANDLER *result_handler, 
+                                       void *ctx)
+{
+   SQL_ROW row;
+   bool retval = false;
+   bool in_transaction = m_transaction;
+   
+   Dmsg1(500, "db_sql_query starts with '%s'\n", query);
+
+   /* This code handles only SELECT queries */
+   if (strncasecmp(query, "SELECT", 6) != 0) {
+      return db_sql_query(query, result_handler, ctx);
+   }
+
+   db_lock(this);
+
+   if (!result_handler) {       /* no need of big_query without handler */
+      goto bail_out;
+   }
+
+   if (!in_transaction) {       /* CURSOR needs transaction */
+      sql_query("BEGIN");
+   }
+
+   Mmsg(m_buf, "DECLARE _bac_cursor CURSOR FOR %s", query);
+
+   if (!sql_query(m_buf)) {
+      Mmsg(errmsg, _("Query failed: %s: ERR=%s\n"), m_buf, sql_strerror());
+      Dmsg0(50, "db_sql_query failed\n");
+      goto bail_out;
+   }
+
+   do {
+      if (!sql_query("FETCH 100 FROM _bac_cursor")) {
+         goto bail_out;
+      }
+      while ((row = sql_fetch_row()) != NULL) {
+         Dmsg1(500, "Fetching %d rows\n", m_num_rows);
+         if (result_handler(ctx, m_num_fields, row))
+            break;
+      }
+      PQclear(m_result);
+      m_result = NULL;
+      
+   } while (m_num_rows > 0);    /* TODO: Can probably test against 100 */
+
+   sql_free_result();
+
+   if (!in_transaction) {
+      sql_query("COMMIT");  /* end transaction */
+   }
+
+   Dmsg0(500, "db_big_sql_query finished\n");
+   retval = true;
+
+bail_out:
+   db_unlock(this);
+   return retval;
+}
+
 /*
  * Submit a general SQL command (cmd), and for each row returned,
  * the result_handler is called with the ctx.
@@ -592,6 +661,11 @@ SQL_ROW B_DB_POSTGRESQL::sql_fetch_row(void)
 
    Dmsg0(500, "sql_fetch_row start\n");
 
+   if (m_num_fields == 0) {     /* No field, no row */
+      Dmsg0(500, "sql_fetch_row finishes returning NULL, no fields\n");
+      return NULL;
+   }
+
    if (!m_rows || m_rows_size < m_num_fields) {
       if (m_rows) {
          Dmsg0(500, "sql_fetch_row freeing space\n");
index acb5ac4f07832c4ccf767f68b04a8e9f05ac553c..0c824682bfe902875d471f1c5aacb1e6ecb5cb6a 100644 (file)
@@ -116,6 +116,7 @@ enum e_list_type {
    HORZ_LIST,
    VERT_LIST
 };
+
 void db_list_pool_records(JCR *jcr, B_DB *db, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
 void db_list_job_records(JCR *jcr, B_DB *db, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
 void db_list_job_totals(JCR *jcr, B_DB *db, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx);
index 4efc0e813d46e69d10d237782c59eb2bb75b6b8c..8c1105d2c69f0e79d6d64bbfbca7e0ca3bad6db0 100644 (file)
@@ -443,122 +443,133 @@ list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx)
    send(ctx, "\n");
 }
 
-/*
- * If full_list is set, we list vertically, otherwise, we
- * list on one line horizontally.
- */
-void
-list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
+/* Small handler to print the last line of a list xxx command */
+static void last_line_handler(void *vctx, const char *str)
+{
+   LIST_CTX *ctx = (LIST_CTX *)vctx;
+   bstrncat(ctx->line, str, sizeof(ctx->line));
+}
+
+int list_result(void *vctx, int nb_col, char **row)
 {
    SQL_FIELD *field;
-   SQL_ROW row;
    int i, col_len, max_len = 0;
    char buf[2000], ewc[30];
 
-   Dmsg0(800, "list_result starts\n");
-   if (sql_num_rows(mdb) == 0) {
-      send(ctx, _("No results to list.\n"));
-      return;
-   }
+   LIST_CTX *pctx = (LIST_CTX *)vctx;
+   DB_LIST_HANDLER *send = pctx->send;
+   e_list_type type = pctx->type;
+   B_DB *mdb = pctx->mdb;
+   void *ctx = pctx->ctx;
+   JCR *jcr = pctx->jcr;
 
-   Dmsg1(800, "list_result starts looking at %d fields\n", sql_num_fields(mdb));
-   /* determine column display widths */
-   sql_field_seek(mdb, 0);
-   for (i = 0; i < sql_num_fields(mdb); i++) {
-      Dmsg1(800, "list_result processing field %d\n", i);
-      field = sql_fetch_field(mdb);
-      if (!field) {
-         break;
-      }
-      col_len = cstrlen(field->name);
-      if (type == VERT_LIST) {
-         if (col_len > max_len) {
-            max_len = col_len;
+   if (!pctx->once) {
+      pctx->once = true;
+
+      Dmsg1(800, "list_result starts looking at %d fields\n", sql_num_fields(mdb));
+      /* determine column display widths */
+      sql_field_seek(mdb, 0);
+      for (i = 0; i < sql_num_fields(mdb); i++) {
+         Dmsg1(800, "list_result processing field %d\n", i);
+         field = sql_fetch_field(mdb);
+         if (!field) {
+            break;
          }
-      } else {
-         if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
-            field->max_length += (field->max_length - 1) / 3;
-         }  
-         if (col_len < (int)field->max_length) {
-            col_len = field->max_length;
-         }  
-         if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
-            col_len = 4;                 /* 4 = length of the word "NULL" */
+         col_len = cstrlen(field->name);
+         if (type == VERT_LIST) {
+            if (col_len > max_len) {
+               max_len = col_len;
+            }
+         } else {
+            if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
+               field->max_length += (field->max_length - 1) / 3;
+            }  
+            if (col_len < (int)field->max_length) {
+               col_len = field->max_length;
+            }  
+            if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
+               col_len = 4;                 /* 4 = length of the word "NULL" */
+            }
+            field->max_length = col_len;    /* reset column info */
          }
-         field->max_length = col_len;    /* reset column info */
       }
-   }
 
-   Dmsg0(800, "list_result finished first loop\n");
-   if (type == VERT_LIST) {
-      goto vertical_list;
-   }
+      pctx->num_rows++;
 
-   Dmsg1(800, "list_result starts second loop looking at %d fields\n", sql_num_fields(mdb));
-   list_dashes(mdb, send, ctx);
-   send(ctx, "|");
-   sql_field_seek(mdb, 0);
-   for (i = 0; i < sql_num_fields(mdb); i++) {
-      Dmsg1(800, "list_result looking at field %d\n", i);
-      field = sql_fetch_field(mdb);
-      if (!field) {
-         break;
+      Dmsg0(800, "list_result finished first loop\n");
+      if (type == VERT_LIST) {
+         goto vertical_list;
       }
-      max_len = max_length(field->max_length);
-      bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
-      send(ctx, buf);
-   }
-   send(ctx, "\n");
-   list_dashes(mdb, send, ctx);
 
-   Dmsg1(800, "list_result starts third loop looking at %d fields\n", sql_num_fields(mdb));
-   while ((row = sql_fetch_row(mdb)) != NULL) {
-      sql_field_seek(mdb, 0);
+      Dmsg1(800, "list_result starts second loop looking at %d fields\n", 
+            sql_num_fields(mdb));
+
+      /* Keep the result to display the same line at the end of the table */
+      list_dashes(mdb, last_line_handler, pctx);
+      send(ctx, pctx->line);
+
       send(ctx, "|");
+      sql_field_seek(mdb, 0);
       for (i = 0; i < sql_num_fields(mdb); i++) {
+         Dmsg1(800, "list_result looking at field %d\n", i);
          field = sql_fetch_field(mdb);
          if (!field) {
             break;
          }
          max_len = max_length(field->max_length);
-         if (row[i] == NULL) {
-            bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
-         } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
-            bsnprintf(buf, sizeof(buf), " %*s |", max_len,
-                      add_commas(row[i], ewc));
-         } else {
-            bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
-         }
+         bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
          send(ctx, buf);
       }
       send(ctx, "\n");
+      list_dashes(mdb, send, ctx);      
+   }
+   
+   Dmsg1(800, "list_result starts third loop looking at %d fields\n", 
+         sql_num_fields(mdb));
+
+   sql_field_seek(mdb, 0);
+   send(ctx, "|");
+   for (i = 0; i < sql_num_fields(mdb); i++) {
+      field = sql_fetch_field(mdb);
+      if (!field) {
+         break;
+      }
+      max_len = max_length(field->max_length);
+      if (row[i] == NULL) {
+         bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
+      } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
+         bsnprintf(buf, sizeof(buf), " %*s |", max_len,
+                   add_commas(row[i], ewc));
+      } else {
+         bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
+      }
+      send(ctx, buf);
    }
-   list_dashes(mdb, send, ctx);
-   return;
+   send(ctx, "\n");
+   return 0;
 
 vertical_list:
 
    Dmsg1(800, "list_result starts vertical list at %d fields\n", sql_num_fields(mdb));
-   while ((row = sql_fetch_row(mdb)) != NULL) {
-      sql_field_seek(mdb, 0);
-      for (i = 0; i < sql_num_fields(mdb); i++) {
-         field = sql_fetch_field(mdb);
-         if (!field) {
-            break;
-         }
-         if (row[i] == NULL) {
-            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
-         } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
-            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
-                add_commas(row[i], ewc));
-         } else {
-            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
-         }
-         send(ctx, buf);
+
+   sql_field_seek(mdb, 0);
+   for (i = 0; i < sql_num_fields(mdb); i++) {
+      field = sql_fetch_field(mdb);
+      if (!field) {
+         break;
       }
-      send(ctx, "\n");
+      if (row[i] == NULL) {
+         bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
+      } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
+         bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
+                   add_commas(row[i], ewc));
+      } else {
+         bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
+      }
+      send(ctx, buf);
    }
-   return;
+   send(ctx, "\n");
+   return 0;
 }
 
 /* 
index 3d11c08048e87d974a9091a6a1b78ac2aec99989..7fee824ad79998ae88d6d87b35bdbbd71d1897d8 100644 (file)
@@ -1158,7 +1158,7 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids,
 
    Dmsg1(100, "q=%s\n", buf.c_str());
 
-   return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
+   return db_big_sql_query(mdb, buf.c_str(), result_handler, ctx);
 }
 
 /**
index 2e4590be6dcac097004f7985c68e498687b3f1f3..a86195ce36d2fe6c0699e48d7105b3d04269df04 100644 (file)
@@ -121,6 +121,11 @@ bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handle
    return mdb->db_sql_query(query, result_handler, ctx);
 }
 
+bool db_big_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
+{
+   return mdb->db_big_sql_query(query, result_handler, ctx);
+}
+
 void sql_free_result(B_DB *mdb)
 {
    ((B_DB_PRIV *)mdb)->sql_free_result();
index 14e3945408f2749b01ce4c3b4b9f8de9c8eb2317..ecfad4f25c1c7070f57522f200b4b27ebfcd14b8 100644 (file)
@@ -53,6 +53,7 @@ void db_start_transaction(JCR *jcr, B_DB *mdb);
 void db_end_transaction(JCR *jcr, B_DB *mdb);
 bool db_sql_query(B_DB *mdb, const char *query, int flags=0);
 bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx);
+bool db_big_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx);
 
 #ifdef _BDB_PRIV_INTERFACE_
 void sql_free_result(B_DB *mdb);
index d3770872ba9967e8a840cf96b59c8a1d2ab00ef0..05fb1c5cbca136ab2cafc9e4102ac06d95b01554 100644 (file)
 int db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit,
                       void *ctx, int verbose, e_list_type type)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
-   if (!sql_query(mdb, query, QF_STORE_RESULT)) {
+
+   if (!db_big_sql_query(mdb, query, list_result, &lctx)) {
       Mmsg(mdb->errmsg, _("Query failed: %s\n"), sql_strerror(mdb));
       if (verbose) {
          sendit(ctx, mdb->errmsg);
@@ -65,7 +68,8 @@ int db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *s
       return 0;
    }
 
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
+
    sql_free_result(mdb);
    db_unlock(mdb);
    return 1;
@@ -75,6 +79,8 @@ void
 db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr,
                      DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
    if (type == VERT_LIST) {
       if (pdbr->Name[0] != 0) {
@@ -100,12 +106,12 @@ db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr,
       }
    }
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
 
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -114,6 +120,8 @@ db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr,
 void
 db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
    if (type == VERT_LIST) {
       Mmsg(mdb->cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,"
@@ -124,12 +132,11 @@ db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx,
          "FROM Client ORDER BY ClientId");
    }
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
-
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -145,6 +152,8 @@ db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr,
                       DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
    char ed1[50];
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
    if (type == VERT_LIST) {
       if (mdbr->VolumeName[0] != 0) {
@@ -182,12 +191,12 @@ db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr,
       }
    }
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
 
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -197,6 +206,8 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
    char ed1[50];
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
    if (type == VERT_LIST) {
       if (JobId > 0) {                   /* do by JobId */
@@ -222,12 +233,13 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId");
       }
    }
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
 
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -237,6 +249,7 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
 void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
                             DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
    POOL_MEM str_limit(PM_MESSAGE);
    POOL_MEM str_jobids(PM_MESSAGE);
 
@@ -259,20 +272,18 @@ void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
     "WHERE Job.Type = '%c' %s ORDER BY Job.PriorJobId DESC %s",
         (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
-      goto bail_out;
+   if (JobIds && JobIds[0]) {
+      sendit(ctx, _("These JobIds have copies as follows:\n"));
+   } else {
+      sendit(ctx, _("The catalog contains copies as follows:\n"));
    }
 
-   if (sql_num_rows(mdb)) {
-      if (JobIds && JobIds[0]) {
-         sendit(ctx, _("These JobIds have copies as follows:\n"));
-      } else {
-         sendit(ctx, _("The catalog contains copies as follows:\n"));
-      }
-
-      list_result(jcr, mdb, sendit, ctx, type);
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
+      goto bail_out;
    }
 
+   lctx.send_dashes();
+
    sql_free_result(mdb);
 
 bail_out:
@@ -283,6 +294,7 @@ void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
    char ed1[50];
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
 
    if (JobId <= 0) {
       return;
@@ -295,11 +307,12 @@ void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
       Mmsg(mdb->cmd, "SELECT LogText FROM Log "
            "WHERE Log.JobId=%s", edit_int64(JobId, ed1));
    }
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       goto bail_out;
    }
 
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
 
@@ -320,6 +333,8 @@ db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
 {
    char ed1[50];
    char limit[100];
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
+
    db_lock(mdb);
    if (jr->limit > 0) {
       snprintf(limit, sizeof(limit), " LIMIT %d", jr->limit);
@@ -370,11 +385,12 @@ db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
            "FROM Job ORDER BY StartTime,JobId ASC%s", limit);
       }
    }
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
-   list_result(jcr, mdb, sendit, ctx, type);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -387,18 +403,19 @@ db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
 void
 db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
+
    db_lock(mdb);
 
    /* List by Job */
    Mmsg(mdb->cmd, "SELECT  count(*) AS Jobs,sum(JobFiles) "
       "AS Files,sum(JobBytes) AS Bytes,Name AS Job FROM Job GROUP BY Name");
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
-
-   list_result(jcr, mdb, sendit, ctx, HORZ_LIST);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
 
@@ -406,12 +423,13 @@ db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, vo
    Mmsg(mdb->cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
         "AS Files,sum(JobBytes) As Bytes FROM Job");
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   lctx.empty();
+   if (!db_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
 
-   list_result(jcr, mdb, sendit, ctx, HORZ_LIST);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -420,7 +438,9 @@ db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, vo
 void
 db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
    char ed1[50];
+
    db_lock(mdb);
 
    /*
@@ -452,12 +472,12 @@ db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendi
            edit_int64(jobid, ed1), ed1);
    }
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
-
-   list_result(jcr, mdb, sendit, ctx, HORZ_LIST);
+   
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);
@@ -466,7 +486,9 @@ db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendi
 void
 db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
 {
+   LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
    char ed1[50];
+
    db_lock(mdb);
 
    /*
@@ -490,12 +512,12 @@ db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *
            edit_int64(jobid, ed1));
    }
 
-   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+   if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
       db_unlock(mdb);
       return;
    }
 
-   list_result(jcr, mdb, sendit, ctx, HORZ_LIST);
+   lctx.send_dashes();
 
    sql_free_result(mdb);
    db_unlock(mdb);