ROSE  0.9.6a
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SqlDatabase.h
Go to the documentation of this file.
1 #ifndef ROSE_SqlDatabase_H
2 #define ROSE_SqlDatabase_H
3 
4 #include "FormatRestorer.h"
5 #include "StringUtility.h"
6 
7 #include <boost/enable_shared_from_this.hpp>
8 #include <boost/shared_ptr.hpp>
9 
10 #include <cassert>
11 #include <iomanip>
12 #include <stdexcept>
13 #include <stdint.h>
14 #include <string>
15 #include <sstream>
16 #include <vector>
17 
116 namespace SqlDatabase {
117 
119 enum Driver {
123 };
124 
125 
126 /*******************************************************************************************************************************
127  * Forwards and types
128  *******************************************************************************************************************************/
129 
130 class Connection;
131 class ConnectionImpl;
132 class Transaction;
133 class TransactionImpl;
134 class Statement;
135 class StatementImpl;
136 
139 typedef boost::shared_ptr<Connection> ConnectionPtr;
140 
143 typedef boost::shared_ptr<Transaction> TransactionPtr;
144 
147 typedef boost::shared_ptr<Statement> StatementPtr;
148 
149 // Data type used in templates to indicate lack of a column
150 class NoColumn {};
151 
152 /*******************************************************************************************************************************
153  * Exceptions
154  *******************************************************************************************************************************/
155 
157 class Exception: public std::runtime_error {
158 public:
159  explicit Exception(const char *mesg): std::runtime_error(mesg) {}
160  explicit Exception(const std::string &mesg): std::runtime_error(mesg) {}
161  explicit Exception(const std::runtime_error &e, const ConnectionPtr &conn, const TransactionPtr &tx,
162  const StatementPtr &stmt)
163  : std::runtime_error(e), connection(conn), transaction(tx), statement(stmt) {}
164  explicit Exception(const std::string &mesg, const ConnectionPtr &conn, const TransactionPtr &tx,
165  const StatementPtr &stmt)
166  : std::runtime_error(mesg), connection(conn), transaction(tx), statement(stmt) {}
167 
168  virtual ~Exception() throw() {}
169  virtual const char *what() const throw() /*override*/;
170  void print(std::ostream&) const;
174 private:
175  mutable std::string what_str; // backing store for what()
176 };
177 
178 
179 /*******************************************************************************************************************************
180  * Connections
181  *******************************************************************************************************************************/
182 
189 class Connection: public boost::enable_shared_from_this<Connection> {
190  friend class TransactionImpl;
191  friend class Transaction;
192  friend class StatementImpl;
193 public:
199  static ConnectionPtr create(const std::string &open_spec, Driver driver=NO_DRIVER) {
200  if (NO_DRIVER==driver) {
201  if (NO_DRIVER==(driver = guess_driver(open_spec)))
202  throw Exception("no suitable driver for \""+open_spec+"\"");
203  }
204  return ConnectionPtr(new Connection(open_spec, driver));
205  }
206 
211 
214  static Driver guess_driver(const std::string &open_spec);
215 
217  Driver driver() const;
218 
220  std::string openspec() const;
221 
225  void set_debug(FILE *f);
226  FILE *get_debug() const;
230  void print(std::ostream&) const;
231 
232  // Only called by boost::shared_ptr
233  ~Connection() { finish(); }
234 
235 protected:
236  // Protected because you should be using create() to get a smart pointer. Database driver-level connections are typically
237  // not copyable object anyway.
238  Connection(const std::string &open_spec, Driver driver): impl(NULL) { init(open_spec, driver); }
239 
240 private:
241  void init(const std::string &open_spec, Driver driver);
242  void finish();
243 
244 private:
245  ConnectionImpl *impl;
246 };
247 
248 /*******************************************************************************************************************************
249  * Transactions
250  *******************************************************************************************************************************/
251 
257 class Transaction: public boost::enable_shared_from_this<Transaction> {
258  friend class ConnectionImpl;
259  friend class Statement;
260  friend class StatementImpl;
261 public:
265  static TransactionPtr create(const ConnectionPtr &conn);
266 
270  void rollback();
271 
275  void commit();
276 
279  bool is_terminated() const;
280 
282  StatementPtr statement(const std::string &sql);
283 
287  void execute(const std::string &sql);
288 
292  void bulk_load(const std::string &tablename, std::istream&);
293 
295  Driver driver() const;
296 
298  ConnectionPtr connection() const;
299 
304  void set_debug(FILE *f);
305  FILE *get_debug() const;
309  void print(std::ostream&) const;
310 
311  // Only called by boost::shared_ptr
312  ~Transaction() { finish(); }
313 
314 protected:
315  explicit Transaction(const ConnectionPtr &conn, size_t drv_conn): impl(NULL) { init(conn, drv_conn); }
316 
317 private:
318  void init(const ConnectionPtr &conn, size_t drv_conn);
319  void finish();
320 
321 private:
322  TransactionImpl *impl;
323 };
324 
325 
326 /*******************************************************************************************************************************
327  * Statements
328  *******************************************************************************************************************************/
329 
338 class Statement: public boost::enable_shared_from_this<Statement> {
339 public:
340 
343  static StatementPtr create(const TransactionPtr &tranx, const std::string &sql) {
344  return StatementPtr(new Statement(tranx, sql));
345  }
346 
350  class iterator {
351  public:
352  iterator(): execution_seq(0), row_num(0) { init(); } // an end iterator
353  iterator(const StatementPtr &stmt, size_t execution_seq)
354  : stmt(stmt), execution_seq(execution_seq), row_num(0) { init(); }
355  template<typename T> T get(size_t idx);
356  int32_t get_i32(size_t idx);
357  int64_t get_i64(size_t idx);
358  uint32_t get_u32(size_t idx);
359  uint64_t get_u64(size_t idx);
360  double get_dbl(size_t idx);
361  std::string get_str(size_t idx);
362  iterator& operator++();
363  bool at_eof() const;
364  bool operator==(const iterator &other) const;
365  bool operator!=(const iterator &other) const { return !(*this==other); }
366  private:
367  void init();
368  void check() const; // check that this iterator is not stale
369  private:
370  StatementPtr stmt; // statement whose results we are iterating over
371  size_t execution_seq; // statement execution counter
372  size_t row_num; // row number
373  };
374 
379  StatementPtr bind(size_t idx, int32_t val);
380  StatementPtr bind(size_t idx, int64_t val);
381  StatementPtr bind(size_t idx, uint32_t val);
382  StatementPtr bind(size_t idx, uint64_t val);
383  StatementPtr bind(size_t idx, double val);
384  StatementPtr bind(size_t idx, const std::string &val);
388  iterator begin();
389 
392  iterator end() { return iterator(); }
393 
395  void execute();
396 
398  int execute_int();
399 
401  Driver driver() const;
402 
404  TransactionPtr transaction() const;
405 
410  void set_debug(FILE *f);
411  FILE *get_debug() const;
415  void print(std::ostream&) const;
416 
417 public:
418  // Called only by boost::shared_ptr
419  ~Statement() { finish(); }
420 
421 protected:
422  Statement(const TransactionPtr &tranx, const std::string &sql): impl(NULL) { init(tranx, sql); }
423 
424 private:
425  void init(const TransactionPtr &tranx, const std::string &sql);
426  void finish();
427 
428 private:
429  StatementImpl *impl;
430 };
431 
432 template<> NoColumn Statement::iterator::get<NoColumn>(size_t idx);
433 template<> int64_t Statement::iterator::get<int64_t>(size_t idx);
434 template<> uint64_t Statement::iterator::get<uint64_t>(size_t idx);
435 template<> int32_t Statement::iterator::get<int32_t>(size_t idx);
436 template<> uint32_t Statement::iterator::get<uint32_t>(size_t idx);
437 template<> float Statement::iterator::get<float>(size_t idx);
438 template<> double Statement::iterator::get<double>(size_t idx);
439 template<> std::string Statement::iterator::get<std::string>(size_t idx);
440 
441 /*******************************************************************************************************************************
442  * Miscellaneous functions
443  *******************************************************************************************************************************/
444 
446 std::vector<std::string> split_sql(const std::string &sql);
447 
449 std::string escape(const std::string&, Driver, bool do_quote=true);
450 
452 bool is_valid_table_name(const std::string &name);
453 
455 template<class Container>
456 std::string in(const Container &values)
457 {
458  std::ostringstream retval;
459  retval <<"in (";
460  unsigned nvals = 0;
461  for (typename Container::const_iterator vi=values.begin(); vi!=values.end(); ++vi, ++nvals)
462  retval <<(nvals?", ":"") <<*vi;
463  retval <<")";
464  return retval.str();
465 }
466 
468 template<class Container, class Stringifier>
469 std::string in_numbers(const Container &values, Stringifier &stringifier)
470 {
471  return "in (" + StringUtility::join(", ", StringUtility::toStrings(values, stringifier)) + ")";
472 }
473 
476 template<class Container>
477 std::string in_strings(const Container &values, Driver driver)
478 {
479  std::vector<std::string> strings;
480  for (typename Container::const_iterator vi=values.begin(); vi!=values.end(); ++vi)
481  strings.push_back(escape(*vi, driver));
482  return in(strings);
483 }
484 
485 std::ostream& operator<<(std::ostream&, const NoColumn&);
486 std::ostream& operator<<(std::ostream&, const Exception&);
487 std::ostream& operator<<(std::ostream&, const Connection&);
488 std::ostream& operator<<(std::ostream&, const Transaction&);
489 std::ostream& operator<<(std::ostream&, const Statement&);
490 
491 /*******************************************************************************************************************************
492  * Tables
493  *******************************************************************************************************************************/
494 
495 // The things here that don't have doxygen comments are not intended to be used directly by users.
496 
497 template<typename> struct ColumnTraits {
498  enum { valid_column = 1 };
499 };
500 template<> struct ColumnTraits<NoColumn> {
501  enum { valid_column = 0 };
502 };
503 
505 template<typename T>
506 class Renderer {
507 public:
508  virtual ~Renderer() {}
509 
512  virtual std::string operator()(const T &value, size_t width) const {
513  std::ostringstream ss;
514  ss <<value;
515  return ss.str();
516  }
517 };
518 
519 // Specialized for string-valued columns in order to left-justify the string by padding it on the right with an
520 // appropriate number of SPC characters.
521 template<>
522 class Renderer<std::string> {
523 public:
524  virtual ~Renderer() {}
525  virtual std::string operator()(const std::string &value, size_t width) const {
526  return value + std::string(width, ' ');
527  }
528 };
529 
531 struct AddrRenderer: Renderer<uint64_t> {
532  size_t nbits; // number of bits in addresses; higher addresses will result in more digits
533  explicit AddrRenderer(size_t nbits=32): nbits(nbits) {}
534  virtual std::string operator()(const uint64_t &value, size_t width) const /*override*/ {
535  return StringUtility::addrToString(value, nbits);
536  }
537 };
538 extern AddrRenderer addr8Renderer;
539 extern AddrRenderer addr16Renderer;
540 extern AddrRenderer addr32Renderer;
541 extern AddrRenderer addr64Renderer;
544 struct TimeRenderer: Renderer<time_t> {
545  bool local_tz;
546  std::string format;
547  TimeRenderer(): local_tz(true), format("%F %T %z") {}
548  explicit TimeRenderer(const std::string &format, bool local_tz=true): local_tz(local_tz), format(format) {}
549  virtual std::string operator()(const time_t &value, size_t width) const /*override*/;
550 };
551 extern TimeRenderer timeRenderer;
552 extern TimeRenderer dateRenderer;
553 extern TimeRenderer humanTimeRenderer;
556 template<typename Table>
557 class PrePostRow {
558 public:
559  virtual ~PrePostRow() {}
560  virtual void operator()(std::ostream &out, const Table *table, size_t rownum, const std::vector<size_t> &widths) {}
561 };
562 
563 // Prints a header
564 template<typename T>
565 class Header {
566 public:
567  void operator()(std::ostream &out, const std::string &prefix, const std::string &hdr, size_t width) {
568  FormatRestorer fr(out);
569  out <<prefix <<std::setw(width) <<hdr;
570  }
571 };
572 template<>
573 class Header<std::string> {
574 public:
575  void operator()(std::ostream &out, const std::string &prefix, const std::string &hdr, size_t width) {
576  FormatRestorer fr(out);
577  out <<prefix <<std::setw(width) <<std::left <<hdr;
578  }
579 };
580 template<>
581 class Header<NoColumn> {
582 public:
583  void operator()(std::ostream &out, const std::string &prefix, const std::string &hdr, size_t width) {}
584 };
585 
586 // Prints a row separator for a single column
587 template<typename T>
589 public:
590  void operator()(std::ostream &out, const std::string &prefix, size_t width) {
591  out <<prefix <<std::string(width, '-');
592  }
593 };
594 template<>
596 public:
597  void operator()(std::ostream &out, const std::string &prefix, size_t width) {}
598 };
599 
673 template<typename T00, typename T01=NoColumn, typename T02=NoColumn, typename T03=NoColumn,
674  typename T04=NoColumn, typename T05=NoColumn, typename T06=NoColumn, typename T07=NoColumn,
675  typename T08=NoColumn, typename T09=NoColumn, typename T10=NoColumn, typename T11=NoColumn,
676  typename T12=NoColumn, typename T13=NoColumn, typename T14=NoColumn, typename T15=NoColumn>
677 class Table {
678 public:
679  enum {
680  MAXCOLS=16,
681  };
682 
684  struct Tuple { // We could use boost::tuple, but it has a 10-member limit.
685  explicit Tuple(const T00 &v0, const T01 &v1 =T01(), const T02 &v2 =T02(), const T03 &v3 =T03(),
686  const T04 &v4 =T04(), const T05 &v5 =T05(), const T06 &v6 =T06(), const T07 &v7 =T07(),
687  const T08 &v8 =T08(), const T09 &v9 =T09(), const T10 &v10=T10(), const T11 &v11=T11(),
688  const T12 &v12=T12(), const T13 &v13=T13(), const T14 &v14=T14(), const T15 &v15=T15())
689  : v0(v0), v1(v1), v2(v2), v3(v3), v4(v4), v5(v5), v6(v6), v7(v7),
690  v8(v8), v9(v9), v10(v10), v11(v11), v12(v12), v13(v13), v14(v14), v15(v15) {}
691  T00 v0; T01 v1; T02 v2; T03 v3; T04 v4; T05 v5; T06 v6; T07 v7;
692  T08 v8; T09 v9; T10 v10; T11 v11; T12 v12; T13 v13; T14 v14; T15 v15;
693  };
694 
696  struct Renderers { // We could use boost::tuple, but it has a 10-member limit.
697  explicit Renderers(const Renderer<T00> *r0 =NULL, const Renderer<T01> *r1 =NULL,
698  const Renderer<T02> *r2 =NULL, const Renderer<T03> *r3 =NULL,
699  const Renderer<T04> *r4 =NULL, const Renderer<T05> *r5 =NULL,
700  const Renderer<T06> *r6 =NULL, const Renderer<T07> *r7 =NULL,
701  const Renderer<T08> *r8 =NULL, const Renderer<T09> *r9 =NULL,
702  const Renderer<T10> *r10=NULL, const Renderer<T11> *r11=NULL,
703  const Renderer<T12> *r12=NULL, const Renderer<T13> *r13=NULL,
704  const Renderer<T14> *r14=NULL, const Renderer<T15> *r15=NULL)
705  : r0(r0), r1(r1), r2(r2), r3(r3), r4(r4), r5(r5), r6(r6), r7(r7),
706  r8(r8), r9(r9), r10(r10), r11(r11), r12(r12), r13(r13), r14(r14), r15(r15) {}
707  const Renderer<T00> *r0; const Renderer<T01> *r1; const Renderer<T02> *r2; const Renderer<T03> *r3;
708  const Renderer<T04> *r4; const Renderer<T05> *r5; const Renderer<T06> *r6; const Renderer<T07> *r7;
709  const Renderer<T08> *r8; const Renderer<T09> *r9; const Renderer<T10> *r10; const Renderer<T11> *r11;
710  const Renderer<T12> *r12; const Renderer<T13> *r13; const Renderer<T14> *r14; const Renderer<T15> *r15;
711  };
712 
714  typedef std::vector<Tuple> Rows;
715 
717  Table(): headers_(MAXCOLS), colsep_(" | "), pre_row_(NULL), post_row_(NULL), reprint_headers_(0) {}
718 
720  explicit Table(const StatementPtr &stmt)
721  : headers_(MAXCOLS), colsep_(" | "), pre_row_(NULL), post_row_(NULL), reprint_headers_(0) {
722  insert(stmt);
723  }
724 
727  void header(int colnum, const std::string &hdr) {
728  assert(colnum<headers_.size());
729  headers_[colnum] = hdr;
730  }
731  const std::string& header(int colnum) const {
732  assert(colnum<headers_.size());
733  return headers_[colnum];
734  }
735  void headers(const std::string &h0, const std::string &h1="", const std::string &h2="", const std::string &h3="",
736  const std::string &h4="", const std::string &h5="", const std::string &h6="", const std::string &h7="",
737  const std::string &h8="", const std::string &h9="", const std::string &h10="", const std::string &h11="",
738  const std::string &h12="", const std::string &h13="", const std::string &h14="", const std::string &h15="") {
739  headers_[ 0] = h0; headers_[ 1] = h1; headers_[ 2] = h2; headers_[ 3] = h3;
740  headers_[ 4] = h4; headers_[ 5] = h5; headers_[ 6] = h6; headers_[ 7] = h7;
741  headers_[ 8] = h8; headers_[ 9] = h9; headers_[10] = h10; headers_[11] = h11;
742  headers_[12] = h12; headers_[13] = h13; headers_[14] = h14; headers_[15] = h15;
743  }
748  void reprint_headers(size_t nrows) { reprint_headers_ = nrows; }
749  size_t reprint_headers() const { return reprint_headers_; }
768  Renderers& renderers() { return render_; }
769  const Renderers& renderers() const { return render_; }
775  pre_row_ = pre;
776  post_row_ = post;
777  }
778  std::pair<PrePostRow<Table>*, PrePostRow<Table>*> prepost() const {
779  return std::make_pair(pre_row_, post_row_);
780  }
784  size_t size() const { return rows_.size(); }
785 
787  bool empty() const { return rows_.empty(); }
788 
790  void clear() { rows_.clear(); }
791 
794  Tuple& operator[](size_t i) { assert(i<rows_.size()); return rows_[i]; }
795  const Tuple& operator[](size_t i) const { assert(i<rows_.size()); return rows_[i]; }
801  void insert(const Tuple &tuple) { rows_.push_back(tuple); }
802  void insert(const T00 &v00, const T01 &v01=T01(), const T02 &v02=T02(), const T03 &v03=T03(),
803  const T04 &v04=T04(), const T05 &v05=T05(), const T06 &v06=T06(), const T07 &v07=T07(),
804  const T08 &v08=T08(), const T09 &v09=T09(), const T10 &v10=T10(), const T11 &v11=T11(),
805  const T12 &v12=T12(), const T13 &v13=T13(), const T14 &v14=T14(), const T15 &v15=T15()) {
806  rows_.push_back(Tuple(v00, v01, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14, v15));
807  }
811  void insert(const StatementPtr &stmt) {
812  for (Statement::iterator row=stmt->begin(); row!=stmt->end(); ++row) {
813  rows_.push_back(Tuple(row.get<T00>( 0), row.get<T01>( 1), row.get<T02>( 2), row.get<T03>( 3),
814  row.get<T04>( 4), row.get<T05>( 5), row.get<T06>( 6), row.get<T07>( 7),
815  row.get<T08>( 8), row.get<T09>( 9), row.get<T10>(10), row.get<T11>(11),
816  row.get<T12>(12), row.get<T13>(13), row.get<T14>(14), row.get<T15>(15)));
817  }
818  }
819 
822  void line_prefix(const std::string &s) { prefix_ = s; }
823  const std::string& line_prefix() const { return prefix_; }
827  template<typename T>
828  std::string render(const Renderer<T> *r, const T &value, size_t width=0) const {
829  Renderer<T> r_;
830  if (NULL==r)
831  r = &r_;
832  std::string s = (*r)(value, width);
833  if (width!=0)
834  s = s.substr(0, width);
835  return s;
836  }
837 
840  std::vector<size_t> colsizes() const {
841  std::vector<size_t> widths(MAXCOLS, 0);
842  for (typename Rows::const_iterator ri=rows_.begin(); ri!=rows_.end(); ++ri) {
843  widths[ 0] = std::max(widths[ 0], render(render_.r0, ri->v0 ).size());
844  widths[ 1] = std::max(widths[ 1], render(render_.r1, ri->v1 ).size());
845  widths[ 2] = std::max(widths[ 2], render(render_.r2, ri->v2 ).size());
846  widths[ 3] = std::max(widths[ 3], render(render_.r3, ri->v3 ).size());
847  widths[ 4] = std::max(widths[ 4], render(render_.r4, ri->v4 ).size());
848  widths[ 5] = std::max(widths[ 5], render(render_.r5, ri->v5 ).size());
849  widths[ 6] = std::max(widths[ 6], render(render_.r6, ri->v6 ).size());
850  widths[ 7] = std::max(widths[ 7], render(render_.r7, ri->v7 ).size());
851  widths[ 8] = std::max(widths[ 8], render(render_.r8, ri->v8 ).size());
852  widths[ 9] = std::max(widths[ 9], render(render_.r9, ri->v9 ).size());
853  widths[10] = std::max(widths[10], render(render_.r10, ri->v10).size());
854  widths[11] = std::max(widths[11], render(render_.r11, ri->v11).size());
855  widths[12] = std::max(widths[12], render(render_.r12, ri->v12).size());
856  widths[13] = std::max(widths[13], render(render_.r13, ri->v13).size());
857  widths[14] = std::max(widths[14], render(render_.r14, ri->v14).size());
858  widths[15] = std::max(widths[15], render(render_.r15, ri->v15).size());
859  }
860  return widths;
861  }
862 
864  void print_headers(std::ostream &out, const std::vector<size_t> &widths) const {
865  Header<T00>()(out, prefix_, headers_[ 0], widths[ 0]);
866  Header<T01>()(out, colsep_, headers_[ 1], widths[ 1]);
867  Header<T02>()(out, colsep_, headers_[ 2], widths[ 2]);
868  Header<T03>()(out, colsep_, headers_[ 3], widths[ 3]);
869  Header<T04>()(out, colsep_, headers_[ 4], widths[ 4]);
870  Header<T05>()(out, colsep_, headers_[ 5], widths[ 5]);
871  Header<T06>()(out, colsep_, headers_[ 6], widths[ 6]);
872  Header<T07>()(out, colsep_, headers_[ 7], widths[ 7]);
873  Header<T08>()(out, colsep_, headers_[ 8], widths[ 8]);
874  Header<T09>()(out, colsep_, headers_[ 9], widths[ 9]);
875  Header<T10>()(out, colsep_, headers_[10], widths[10]);
876  Header<T11>()(out, colsep_, headers_[11], widths[11]);
877  Header<T12>()(out, colsep_, headers_[12], widths[12]);
878  Header<T13>()(out, colsep_, headers_[13], widths[13]);
879  Header<T14>()(out, colsep_, headers_[14], widths[14]);
880  Header<T15>()(out, colsep_, headers_[15], widths[15]);
881  out <<"\n";
882  }
883 
885  void print_rowsep(std::ostream &out, const std::vector<size_t> &widths) const {
886  std::string colsep = colsep_;
887  for (size_t i=0; i<colsep.size(); ++i) {
888  if (isspace(colsep[i])) {
889  colsep[i] = '-';
890  } else if ('|'==colsep[i]) {
891  colsep[i] = '+';
892  }
893  }
894  RowSeparator<T00>()(out, prefix_, widths[ 0]);
895  RowSeparator<T01>()(out, colsep, widths[ 1]);
896  RowSeparator<T02>()(out, colsep, widths[ 2]);
897  RowSeparator<T03>()(out, colsep, widths[ 3]);
898  RowSeparator<T04>()(out, colsep, widths[ 4]);
899  RowSeparator<T05>()(out, colsep, widths[ 5]);
900  RowSeparator<T06>()(out, colsep, widths[ 6]);
901  RowSeparator<T07>()(out, colsep, widths[ 7]);
902  RowSeparator<T08>()(out, colsep, widths[ 8]);
903  RowSeparator<T09>()(out, colsep, widths[ 9]);
904  RowSeparator<T10>()(out, colsep, widths[10]);
905  RowSeparator<T11>()(out, colsep, widths[11]);
906  RowSeparator<T12>()(out, colsep, widths[12]);
907  RowSeparator<T13>()(out, colsep, widths[13]);
908  RowSeparator<T14>()(out, colsep, widths[14]);
909  RowSeparator<T15>()(out, colsep, widths[15]);
910  out <<"\n";
911  }
912 
914  void
915  print_row(std::ostream &out, const Tuple &t, const std::vector<size_t> &widths) const {
916  FormatRestorer fr(out);
917  out << prefix_ <<std::setw(widths[ 0]) <<render(render_.r0, t.v0, widths[ 0])
918  <<(ColumnTraits<T01>::valid_column ? colsep_ : "") <<std::setw(widths[ 1]) <<render(render_.r1, t.v1, widths[ 1])
919  <<(ColumnTraits<T02>::valid_column ? colsep_ : "") <<std::setw(widths[ 2]) <<render(render_.r2, t.v2, widths[ 2])
920  <<(ColumnTraits<T03>::valid_column ? colsep_ : "") <<std::setw(widths[ 3]) <<render(render_.r3, t.v3, widths[ 3])
921  <<(ColumnTraits<T04>::valid_column ? colsep_ : "") <<std::setw(widths[ 4]) <<render(render_.r4, t.v4, widths[ 4])
922  <<(ColumnTraits<T05>::valid_column ? colsep_ : "") <<std::setw(widths[ 5]) <<render(render_.r5, t.v5, widths[ 5])
923  <<(ColumnTraits<T06>::valid_column ? colsep_ : "") <<std::setw(widths[ 6]) <<render(render_.r6, t.v6, widths[ 6])
924  <<(ColumnTraits<T07>::valid_column ? colsep_ : "") <<std::setw(widths[ 7]) <<render(render_.r7, t.v7, widths[ 7])
925  <<(ColumnTraits<T08>::valid_column ? colsep_ : "") <<std::setw(widths[ 8]) <<render(render_.r8, t.v8, widths[ 8])
926  <<(ColumnTraits<T09>::valid_column ? colsep_ : "") <<std::setw(widths[ 9]) <<render(render_.r9, t.v9, widths[ 9])
927  <<(ColumnTraits<T10>::valid_column ? colsep_ : "") <<std::setw(widths[10]) <<render(render_.r10, t.v10, widths[10])
928  <<(ColumnTraits<T11>::valid_column ? colsep_ : "") <<std::setw(widths[11]) <<render(render_.r11, t.v11, widths[11])
929  <<(ColumnTraits<T12>::valid_column ? colsep_ : "") <<std::setw(widths[12]) <<render(render_.r12, t.v12, widths[12])
930  <<(ColumnTraits<T13>::valid_column ? colsep_ : "") <<std::setw(widths[13]) <<render(render_.r13, t.v13, widths[13])
931  <<(ColumnTraits<T14>::valid_column ? colsep_ : "") <<std::setw(widths[14]) <<render(render_.r14, t.v14, widths[14])
932  <<(ColumnTraits<T15>::valid_column ? colsep_ : "") <<std::setw(widths[15]) <<render(render_.r15, t.v15, widths[15])
933  <<"\n";
934  }
935 
937  void print(std::ostream &out) const {
938  std::vector<size_t> widths = colsizes();
939 
940  // Headers
941  bool has_headers = false;
942  for (size_t i=0; i<MAXCOLS; ++i) {
943  widths[i] = std::max(widths[i], headers_[i].size());
944  if (!headers_[i].empty())
945  has_headers = true;
946  }
947  if (has_headers) {
948  print_headers(out, widths);
949  print_rowsep(out, widths);
950  }
951 
952  // Body
953  if (rows_.empty()) {
954  out <<prefix_ <<"(0 rows)\n\n";
955  } else {
956  for (size_t i=0; i<rows_.size(); ++i) {
957  if (i>0 && has_headers && reprint_headers_>0 && 0==i%reprint_headers_) {
958  print_rowsep(out, widths);
959  print_headers(out, widths);
960  print_rowsep(out, widths);
961  }
962  if (pre_row_)
963  (*pre_row_)(out, this, i, widths);
964  print_row(out, rows_[i], widths);
965  if (post_row_)
966  (*post_row_)(out, this, i, widths);
967  }
968  }
969 
970  // Epilogue
971  if (has_headers && !rows_.empty())
972  print_rowsep(out, widths);
973  }
974 
975 private:
976  std::vector<std::string> headers_;
979  std::string colsep_;
982  std::string prefix_; // string printed before every line of output
983 };
984 
985 } // namespace
986 #endif