sqlite3_stmt * Connection::prepare( const string &query,
                                    const AttributeList &parameters ) {
  if( db_ == NULL )
    throw ActiveRecordException( "Database not connected", __FILE__, __LINE__ );
  sqlite3_stmt *ppStmt = 0;
  int prepare_result = sqlite3_prepare_v2( db_, query.c_str(), query.size(), &ppStmt, 0 );
  if( prepare_result != SQLITE_OK ) {
    stringstream error;
    error << "SQL error: \"" << sqlite_error( prepare_result ) << "\" ";
    error << "in \"" << query << "\"";
    bool added = false;
    for( AttributeList::const_iterator it = parameters.begin(); it != parameters.end(); ++it ) {
      error << ", ";
      if( ! added )
        error << "[";
      error << *it;
      added = true;
    }
    if( added )
      error << "]";
    log( error.str() );
    throw ActiveRecordException( error.str(), __FILE__, __LINE__ );
  }
  bind_parameters( ppStmt, parameters );
  return ppStmt;
}
Attribute Attribute::from_field(sqlite3_stmt *pStmt, int i) {
  const char * type = sqlite3_column_decltype( pStmt, i );
  if( type == NULL ) {
    // http://www.sqlite.org/capi3ref.html#sqlite3_column_decltype
    // expression or subquery - skip
    const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
    if( value != 0 ) // TODO - throw exception?
      return value;
  } else if( strcasecmp( type, "INTEGER" ) == 0 ) {
    return sqlite3_column_int( pStmt, i );
  } else if( strcasecmp( type, "FLOAT" ) == 0 ) {
    return sqlite3_column_double( pStmt, i );
  } else if( strcasecmp( type, "TEXT" ) == 0 ) {
    const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
    if( value != 0 ) // TODO - throw exception?
      return value;
  } else if( strcasecmp( type, "DATE" ) == 0 ) {
    const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
    if( value != 0 )
      return Date::parse( value );
  } else {
    stringstream error;
    error << "Unhandled data type: " << type;
    throw ActiveRecordException( error.str(), __FILE__, __LINE__ );
  }
}
void Connection::bind_parameters( sqlite3_stmt *ppStmt,
                                  const AttributeList &parameters ) {
  int i = 0;
  for( AttributeList::const_iterator it = parameters.begin();
       it != parameters.end();
       ++it ) {
    switch( it->which() ) {
    case integer: {
      int value = boost::get< int >( *it );
      sqlite3_bind_int( ppStmt, i + 1, value );
      break;
    }
    case text: {
      string value = boost::get< std::string >( *it );
      sqlite3_bind_text( ppStmt, i + 1, value.c_str(), value.size(), 0 );
      break;
    }
    case floating_point: {
      double value = boost::get< double >( *it );
      sqlite3_bind_double( ppStmt, i + 1, value );
      break;
    }
    case date: {
      Date value = boost::get< Date >( *it );
      string s   = value.to_string();
      sqlite3_bind_text( ppStmt, i + 1, s.c_str(), s.size(), 0 );
      break;
    }
    default: {
      throw ActiveRecordException( "Type not implemented", __FILE__, __LINE__ );
    }
    }
    ++i;
  }
}
Row::Row( sqlite3_stmt *pStmt ) {
  int count = sqlite3_column_count( pStmt );
  for( int i = 0; i < count; ++i ) {
    string name = sqlite3_column_name( pStmt, i );
    const char * type = sqlite3_column_decltype( pStmt, i );
    if( type == NULL ) {
      // http://www.sqlite.org/capi3ref.html#sqlite3_column_decltype
      // expression or subquery - skip
      const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
      if ( value != 0 )
        attributes_[ name ] = value;
    } else if( strcasecmp( type, "INTEGER" ) == 0 ) {
      attributes_[ name ] = sqlite3_column_int( pStmt, i );
    } else if( strcasecmp( type, "FLOAT" ) == 0 ) {
      attributes_[ name ] = sqlite3_column_double( pStmt, i );
    } else if( strcasecmp( type, "TEXT" ) == 0 ) {
      const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
      if ( value != 0 )
        attributes_[ name ] = value;
    } else if( strcasecmp( type, "DATE" ) == 0 ) {
      const char * value = ( const char * ) sqlite3_column_text( pStmt, i );
      if ( value != 0 )
        attributes_[ name ] = Date::parse( value );
    }
    else {
      stringstream error;
      error << "Unhandled data type: " << type;
      throw ActiveRecordException( error.str(), __FILE__, __LINE__ );
    }
  }
}
bool Connection::sqlite_initialize( string database_path_name ) {
  int nResult = sqlite3_open( database_path_name.c_str(), &db_ );
  if( nResult ) {
    stringstream error;
    error << "Can't open database '" << database_path_name << "'";
    error << sqlite3_errmsg( db_ );
    sqlite3_close( db_ );
    throw ActiveRecordException( error.str(), __FILE__, __LINE__ );
  }
  return true;
}
void TableSet::update_table( Table &required ) {
  log( "TableSet::update_table" );
  log( required.table_name() );
  Table existing = table_data( required.connection(), required.table_name() );
  Fields missing = required.fields() - existing.fields();
  Fields remove  = existing.fields() - required.fields();
  for( Fields::iterator it = missing.begin(); it != missing.end(); ++it )
    existing.add_field( *it );
  for( Fields::iterator it = remove.begin(); it != remove.end(); ++it ) {
    throw ActiveRecordException( "Table::remove_field not yet implemented", __FILE__, __LINE__ );
    //existing.remove_field( *it );
  }
}
bool Attribute::operator==( const Attribute& other ) const {
  if( which() != other.which() )
    return false;
  switch( which() ) {
  case 0:
    return boost::get< int >( *this ) == boost::get< int >( other );
  case 1:
    return boost::get< string >( *this ) == boost::get< string >( other );
  case 2:
    return boost::get< double >( *this ) == boost::get< double >( other );
  case 3:
    return boost::get< Date >( *this ) == boost::get< Date >( other );
  default:
    throw ActiveRecordException( "Unexpected Attribute type", __FILE__, __LINE__ );
  }
}
Table TableSet::table_data( Connection * connection,
                            const string &table_name ) {
  stringstream row_query;
  row_query << "PRAGMA table_info( \"" << table_name << "\" );";
  Table td( connection, table_name );
  RowSet rows = td.connection()->select_all( row_query.str() );
  for( RowSet::iterator it = rows.begin();
       it != rows.end();
       ++it ) {
    // cid | name | type    | notnull | dflt_value | pk
    //   0 |  bar | INTEGER |       0 |            |  0
    string name      = it->get_text( "name" );
    string type_name = it->get_text( "type" );
    ActiveRecord::Type type = ActiveRecord::to_type( type_name );
    if( type == ActiveRecord::unknown ) {
      stringstream error;
      error << "Unknown type: " << type_name;
      throw ActiveRecordException( error.str(), __FILE__, __LINE__ );
    }
    td.fields().push_back( Field( name, type ) );
  }
  return td;
}