/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

// 
//  select_all.cpp:  Prints all rows of a table
//
//  Usage:  select_all <table_name>+

#include <NdbApi.hpp>
 
// Used for cout
#include <iostream>
using namespace std;
#include <stdio.h>
#include <string.h>

#define APIERROR(error) \
  { cout << "Error in " << __FILE__ << ", line:" << __LINE__ << ", code:" \
         << error.code << ", msg: " << error.message << "." << endl; \
    exit(-1); }

void usage(const char* prg) {
  cout << "Usage: " << prg << " <table name>" << endl;
  cout << "Prints all rows of table named <table name>" << endl;
  exit(0);
}

/*****************************************************************************
 *************************** Result Set Container ****************************
 *****************************************************************************/

/*
 * Container of NdbRecAttr objects.
 * (NdbRecAttr objects are database rows read by a scan operation.)
 */
class ResultSetContainer {
public:
  /**
   * Initialize ResultSetContainer object for table named <tableName>
   * - Allocates memory 
   * - Fetches attribute names from NDB Cluster
   */
  void init(NdbDictionary::Dictionary* dict, const char* tableName);
  
  /**
   * Get no of attributes for stored NdbRecAttr objects
   */
  int getNoOfAttributes() const;

  /**
   * Get NdbRecAttr object no i
   */
  NdbRecAttr* & getAttrStore(int i);

  /**
   * Get attribute name of attribute no i
   */
  const char*  getAttrName(int i) const;
  
  /**
   * Print header of rows
   */
  void header() const;

private:
  int         m_cols;      // No of attributes for stored NdbRecAttr objects
  char        **m_names;   // Names of attributes
  NdbRecAttr  **m_data;    // The actual stored NdbRecAttr objects
};

void ResultSetContainer::init(NdbDictionary::Dictionary * dict, 
			      const char* tableName) 
{
  // Get Table object from NDB (this contains metadata about all tables)
  const NdbDictionary::Table * tab = dict->getTable(tableName);
  
  // Get table id of the table we are interested in
  if (tab == 0) APIERROR(dict->getNdbError()); // E.g. table didn't exist
  
  // Get no of attributes and allocate memory
  m_cols = tab->getNoOfColumns();
  m_names = new char*       [m_cols];
  m_data  = new NdbRecAttr* [m_cols];
  
  // Store all attribute names for the table
  for (int i = 0; i < m_cols; i++) {
    m_names[i] = new char[255];
    snprintf(m_names[i], 255, "%s", tab->getColumn(i)->getName());
  }
}

int          ResultSetContainer::getNoOfAttributes() const {return m_cols;}
NdbRecAttr*& ResultSetContainer::getAttrStore(int i)       {return m_data[i];}
const char*  ResultSetContainer::getAttrName(int i) const  {return m_names[i];}

/*****************************************************************************
 **********************************  MAIN  ***********************************
 *****************************************************************************/

int main(int argc, const char** argv) 
{
  ndb_init();
  Ndb* myNdb = new Ndb("ndbapi_example4"); // Object representing the database
  NdbConnection* myNdbConnection;          // For transactions
  NdbOperation* myNdbOperation;            // For operations
  int check;

  if (argc != 2) {
    usage(argv[0]);
    exit(0);
  }
  const char* tableName = argv[1];

  /*******************************************
   * Initialize NDB and wait until its ready *
   *******************************************/
  if (myNdb->init() == -1) { 
    APIERROR(myNdb->getNdbError());
    exit(-1);
  }

  if (myNdb->waitUntilReady(30) != 0) {
    cout << "NDB was not ready within 30 secs." << endl;
    exit(-1);
  }

  /***************************
   * Define and execute scan *
   ***************************/
  cout << "Select * from " << tableName << endl;

  ResultSetContainer * container = new ResultSetContainer;
  container->init(myNdb->getDictionary(), tableName);
  
  myNdbConnection = myNdb->startTransaction();
  if (myNdbConnection == NULL) APIERROR(myNdb->getNdbError());
  
  myNdbOperation = myNdbConnection->getNdbOperation(tableName);
  if (myNdbOperation == NULL) APIERROR(myNdbConnection->getNdbError());
  
  // Define the operation to be an 'openScanRead' operation.
  check = myNdbOperation->openScanRead(1); 
  if (check == -1) APIERROR(myNdbConnection->getNdbError());
  
  // Set interpreted program to just be the single instruction 
  // 'interpret_exit_ok'.  (This approves all rows of the table.)
  if (myNdbOperation->interpret_exit_ok() == -1) 
    APIERROR(myNdbConnection->getNdbError());
  
  // Get all attribute values of the row
  for(int i = 0; i < container->getNoOfAttributes(); i++){
    if((container->getAttrStore(i) = 
	myNdbOperation->getValue(container->getAttrName(i))) == 0) 
      APIERROR(myNdbConnection->getNdbError());
  }

  // Execute scan operation
  check = myNdbConnection->executeScan();      	        
  if (check == -1) APIERROR(myNdbConnection->getNdbError());
  
  /****************
   * Print header *
   ****************/
  for (int i = 0; i < container->getNoOfAttributes(); i++) 
    cout << container->getAttrName(i) << "\t";
  
  cout << endl;
  for (int i = 0; i < container->getNoOfAttributes(); i++) {
    for (int j = strlen(container->getAttrName(i)); j > 0; j--)
      cout << "-";
    cout << "\t";
  }
  cout << "\n";

  /**************
   * Scan table *
   **************/
  int eof;
  int rows = 0;

  // Print all rows of table
  while ((eof = myNdbConnection->nextScanResult()) == 0) {
    rows++;
    
    for (int i = 0; i < container->getNoOfAttributes(); i++) {
      if (container->getAttrStore(i)->isNULL()) {
	cout << "NULL";
      } else {

	// Element size of value (No of bits per element in attribute value)
	const int  size = container->getAttrStore(i)->attrSize();
	
	// No of elements in an array attribute (Is 1 if non-array attribute)
	const int aSize = container->getAttrStore(i)->arraySize();
	
	switch(container->getAttrStore(i)->attrType()){
	case UnSigned:
	  switch(size) {
	  case 8: cout << container->getAttrStore(i)->u_64_value(); break;
	  case 4: cout << container->getAttrStore(i)->u_32_value(); break;
	  case 2: cout << container->getAttrStore(i)->u_short_value(); break;
	  case 1: cout << (unsigned) container->getAttrStore(i)->u_char_value();
	    break;
	  default: cout << "Unknown size" << endl;
	  }
	  break;
	  
	case Signed:
	  switch(size) {
	  case 8: cout << container->getAttrStore(i)->int64_value(); break;
	  case 4: cout << container->getAttrStore(i)->int32_value(); break;
	  case 2: cout << container->getAttrStore(i)->short_value(); break;
	  case 1: cout << (int) container->getAttrStore(i)->char_value(); break;
	  default: cout << "Unknown size" << endl;
	  }
	  break;
	  
	case String:
	  {
	    char* buf = new char[aSize+1];
	    memcpy(buf, container->getAttrStore(i)->aRef(), aSize);
	    buf[aSize] = 0;
	    cout << buf;
	    delete [] buf;
	  }
	  break;
	  
	case Float:
	  cout << container->getAttrStore(i)->float_value();
	  break;
	  
	default:
	  cout << "Unknown";
	  break;
	}
      }
      cout << "\t";
    }
    cout << endl;
  }
  if (eof == -1) APIERROR(myNdbConnection->getNdbError());
  
  myNdb->closeTransaction(myNdbConnection);
  
  cout << "Selected " << rows << " rows." << endl;
}