/**
 * call-seq:
 *     step( vm ) -> hash | nil
 *
 * Steps through a single result for the given virtual machine. Returns a
 * Hash object. If there was a valid row returned, the hash will contain
 * a <tt>:row</tt> key, which maps to an array of values for that row.
 * In addition, the hash will (nearly) always contain a <tt>:columns</tt>
 * key (naming the columns in the result) and a <tt>:types</tt> key
 * (giving the data types for each column).
 *
 * This will return +nil+ if there was an error previously.
 */
static VALUE
static_api_step( VALUE module, VALUE vm )
{
  sqlite_vm   *vm_ptr;
  const char **values;
  const char **metadata;
  int          columns;
  int          result;
  int          index;
  VALUE        hash;
  VALUE        value;

  GetVM( vm_ptr, vm );
  hash = rb_hash_new();

  result = sqlite_step( vm_ptr,
                        &columns,
                        &values,
                        &metadata );

  switch( result )
  {
    case SQLITE_BUSY:
      static_raise_db_error( result, "busy in step" );

    case SQLITE_ROW:
      value = rb_ary_new2( columns );
      for( index = 0; index < columns; index++ )
      {
        VALUE entry = Qnil;

        if( values[index] != NULL )
          entry = rb_str_new2( values[index] );

        rb_ary_store( value, index, entry  );
      }
      rb_hash_aset( hash, ID2SYM(idRow), value );
      
    case SQLITE_DONE:
      value = rb_ivar_get( vm, idColumns );

      if( value == Qnil )
      {
        value = rb_ary_new2( columns );
        for( index = 0; index < columns; index++ )
        {
          rb_ary_store( value, index, rb_str_new2( metadata[ index ] ) );
        }
        rb_ivar_set( vm, idColumns, value );
      }

      rb_hash_aset( hash, ID2SYM(idColumns), value );

      value = rb_ivar_get( vm, idTypes );

      if( value == Qnil )
      {
        value = rb_ary_new2( columns );
        for( index = 0; index < columns; index++ )
        {
          VALUE item = Qnil;
          if( metadata[ index+columns ] )
            item = rb_str_new2( metadata[ index+columns ] );
          rb_ary_store( value, index, item );
        }
        rb_ivar_set( vm, idTypes, value );
      }

      rb_hash_aset( hash, ID2SYM(idTypes), value );
      break;

    case SQLITE_ERROR:
    case SQLITE_MISUSE:
      {
        char *msg = NULL;
        sqlite_finalize( vm_ptr, &msg );
        RDATA(vm)->dfree = NULL;
        RDATA(vm)->data = NULL;
        static_raise_db_error2( result, &msg );
      }
      /* "raise" doesn't return */

    default:
      static_raise_db_error( -1, "[BUG] unknown result %d from sqlite_step",
        result );
      /* "raise" doesn't return */
  }

  return hash;
}